View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package java.awt;
26  
27  import java.io.IOException;
28  import java.io.ObjectInputStream;
29  import java.util.Vector;
30  import java.util.Enumeration;
31  import java.awt.peer.MenuPeer;
32  import java.awt.event.KeyEvent;
33  import javax.accessibility.*;
34  import sun.awt.AWTAccessor;
35  
36  /**
37   * A <code>Menu</code> object is a pull-down menu component
38   * that is deployed from a menu bar.
39   * <p>
40   * A menu can optionally be a <i>tear-off</i> menu. A tear-off menu
41   * can be opened and dragged away from its parent menu bar or menu.
42   * It remains on the screen after the mouse button has been released.
43   * The mechanism for tearing off a menu is platform dependent, since
44   * the look and feel of the tear-off menu is determined by its peer.
45   * On platforms that do not support tear-off menus, the tear-off
46   * property is ignored.
47   * <p>
48   * Each item in a menu must belong to the <code>MenuItem</code>
49   * class. It can be an instance of <code>MenuItem</code>, a submenu
50   * (an instance of <code>Menu</code>), or a check box (an instance of
51   * <code>CheckboxMenuItem</code>).
52   *
53   * @author Sami Shaio
54   * @see     java.awt.MenuItem
55   * @see     java.awt.CheckboxMenuItem
56   * @since   JDK1.0
57   */
58  public class Menu extends MenuItem implements MenuContainer, Accessible {
59  
60      static {
61          /* ensure that the necessary native libraries are loaded */
62          Toolkit.loadLibraries();
63          if (!GraphicsEnvironment.isHeadless()) {
64              initIDs();
65          }
66  
67          AWTAccessor.setMenuAccessor(
68              new AWTAccessor.MenuAccessor() {
69                  public Vector<MenuComponent> getItems(Menu menu) {
70                      return menu.items;
71                  }
72              });
73      }
74  
75      /**
76       * A vector of the items that will be part of the Menu.
77       *
78       * @serial
79       * @see #countItems()
80       */
81      Vector<MenuComponent> items = new Vector<>();
82  
83      /**
84       * This field indicates whether the menu has the
85       * tear of property or not.  It will be set to
86       * <code>true</code> if the menu has the tear off
87       * property and it will be set to <code>false</code>
88       * if it does not.
89       * A torn off menu can be deleted by a user when
90       * it is no longer needed.
91       *
92       * @serial
93       * @see #isTearOff()
94       */
95      boolean             tearOff;
96  
97      /**
98       * This field will be set to <code>true</code>
99       * if the Menu in question is actually a help
100      * menu.  Otherwise it will be set to <code>
101      * false</code>.
102      *
103      * @serial
104      */
105     boolean             isHelpMenu;
106 
107     private static final String base = "menu";
108     private static int nameCounter = 0;
109 
110     /*
111      * JDK 1.1 serialVersionUID
112      */
113      private static final long serialVersionUID = -8809584163345499784L;
114 
115     /**
116      * Constructs a new menu with an empty label. This menu is not
117      * a tear-off menu.
118      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
119      * returns true.
120      * @see java.awt.GraphicsEnvironment#isHeadless
121      * @since      JDK1.1
122      */
123     public Menu() throws HeadlessException {
124         this("", false);
125     }
126 
127     /**
128      * Constructs a new menu with the specified label. This menu is not
129      * a tear-off menu.
130      * @param       label the menu's label in the menu bar, or in
131      *                   another menu of which this menu is a submenu.
132      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
133      * returns true.
134      * @see java.awt.GraphicsEnvironment#isHeadless
135      */
136     public Menu(String label) throws HeadlessException {
137         this(label, false);
138     }
139 
140     /**
141      * Constructs a new menu with the specified label,
142      * indicating whether the menu can be torn off.
143      * <p>
144      * Tear-off functionality may not be supported by all
145      * implementations of AWT.  If a particular implementation doesn't
146      * support tear-off menus, this value is silently ignored.
147      * @param       label the menu's label in the menu bar, or in
148      *                   another menu of which this menu is a submenu.
149      * @param       tearOff   if <code>true</code>, the menu
150      *                   is a tear-off menu.
151      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
152      * returns true.
153      * @see java.awt.GraphicsEnvironment#isHeadless
154      * @since       JDK1.0.
155      */
156     public Menu(String label, boolean tearOff) throws HeadlessException {
157         super(label);
158         this.tearOff = tearOff;
159     }
160 
161     /**
162      * Construct a name for this MenuComponent.  Called by getName() when
163      * the name is null.
164      */
165     String constructComponentName() {
166         synchronized (Menu.class) {
167             return base + nameCounter++;
168         }
169     }
170 
171     /**
172      * Creates the menu's peer.  The peer allows us to modify the
173      * appearance of the menu without changing its functionality.
174      */
175     public void addNotify() {
176         synchronized (getTreeLock()) {
177             if (peer == null)
178                 peer = Toolkit.getDefaultToolkit().createMenu(this);
179             int nitems = getItemCount();
180             for (int i = 0 ; i < nitems ; i++) {
181                 MenuItem mi = getItem(i);
182                 mi.parent = this;
183                 mi.addNotify();
184             }
185         }
186     }
187 
188     /**
189      * Removes the menu's peer.  The peer allows us to modify the appearance
190      * of the menu without changing its functionality.
191      */
192     public void removeNotify() {
193         synchronized (getTreeLock()) {
194             int nitems = getItemCount();
195             for (int i = 0 ; i < nitems ; i++) {
196                 getItem(i).removeNotify();
197             }
198             super.removeNotify();
199         }
200     }
201 
202     /**
203      * Indicates whether this menu is a tear-off menu.
204      * <p>
205      * Tear-off functionality may not be supported by all
206      * implementations of AWT.  If a particular implementation doesn't
207      * support tear-off menus, this value is silently ignored.
208      * @return      <code>true</code> if this is a tear-off menu;
209      *                         <code>false</code> otherwise.
210      */
211     public boolean isTearOff() {
212         return tearOff;
213     }
214 
215     /**
216       * Get the number of items in this menu.
217       * @return     the number of items in this menu.
218       * @since      JDK1.1
219       */
220     public int getItemCount() {
221         return countItems();
222     }
223 
224     /**
225      * @deprecated As of JDK version 1.1,
226      * replaced by <code>getItemCount()</code>.
227      */
228     @Deprecated
229     public int countItems() {
230         return countItemsImpl();
231     }
232 
233     /*
234      * This is called by the native code, so client code can't
235      * be called on the toolkit thread.
236      */
237     final int countItemsImpl() {
238         return items.size();
239     }
240 
241     /**
242      * Gets the item located at the specified index of this menu.
243      * @param     index the position of the item to be returned.
244      * @return    the item located at the specified index.
245      */
246     public MenuItem getItem(int index) {
247         return getItemImpl(index);
248     }
249 
250     /*
251      * This is called by the native code, so client code can't
252      * be called on the toolkit thread.
253      */
254     final MenuItem getItemImpl(int index) {
255         return (MenuItem)items.elementAt(index);
256     }
257 
258     /**
259      * Adds the specified menu item to this menu. If the
260      * menu item has been part of another menu, removes it
261      * from that menu.
262      *
263      * @param       mi   the menu item to be added
264      * @return      the menu item added
265      * @see         java.awt.Menu#insert(java.lang.String, int)
266      * @see         java.awt.Menu#insert(java.awt.MenuItem, int)
267      */
268     public MenuItem add(MenuItem mi) {
269         synchronized (getTreeLock()) {
270             if (mi.parent != null) {
271                 mi.parent.remove(mi);
272             }
273             items.addElement(mi);
274             mi.parent = this;
275             MenuPeer peer = (MenuPeer)this.peer;
276             if (peer != null) {
277                 mi.addNotify();
278                 peer.addItem(mi);
279             }
280             return mi;
281         }
282     }
283 
284     /**
285      * Adds an item with the specified label to this menu.
286      *
287      * @param       label   the text on the item
288      * @see         java.awt.Menu#insert(java.lang.String, int)
289      * @see         java.awt.Menu#insert(java.awt.MenuItem, int)
290      */
291     public void add(String label) {
292         add(new MenuItem(label));
293     }
294 
295     /**
296      * Inserts a menu item into this menu
297      * at the specified position.
298      *
299      * @param         menuitem  the menu item to be inserted.
300      * @param         index     the position at which the menu
301      *                          item should be inserted.
302      * @see           java.awt.Menu#add(java.lang.String)
303      * @see           java.awt.Menu#add(java.awt.MenuItem)
304      * @exception     IllegalArgumentException if the value of
305      *                    <code>index</code> is less than zero
306      * @since         JDK1.1
307      */
308 
309     public void insert(MenuItem menuitem, int index) {
310         synchronized (getTreeLock()) {
311             if (index < 0) {
312                 throw new IllegalArgumentException("index less than zero.");
313             }
314 
315             int nitems = getItemCount();
316             Vector<MenuItem> tempItems = new Vector<>();
317 
318             /* Remove the item at index, nitems-index times
319                storing them in a temporary vector in the
320                order they appear on the menu.
321             */
322             for (int i = index ; i < nitems; i++) {
323                 tempItems.addElement(getItem(index));
324                 remove(index);
325             }
326 
327             add(menuitem);
328 
329             /* Add the removed items back to the menu, they are
330                already in the correct order in the temp vector.
331             */
332             for (int i = 0; i < tempItems.size()  ; i++) {
333                 add(tempItems.elementAt(i));
334             }
335         }
336     }
337 
338     /**
339      * Inserts a menu item with the specified label into this menu
340      * at the specified position.  This is a convenience method for
341      * <code>insert(menuItem, index)</code>.
342      *
343      * @param       label the text on the item
344      * @param       index the position at which the menu item
345      *                      should be inserted
346      * @see         java.awt.Menu#add(java.lang.String)
347      * @see         java.awt.Menu#add(java.awt.MenuItem)
348      * @exception     IllegalArgumentException if the value of
349      *                    <code>index</code> is less than zero
350      * @since       JDK1.1
351      */
352 
353     public void insert(String label, int index) {
354         insert(new MenuItem(label), index);
355     }
356 
357     /**
358      * Adds a separator line, or a hypen, to the menu at the current position.
359      * @see         java.awt.Menu#insertSeparator(int)
360      */
361     public void addSeparator() {
362         add("-");
363     }
364 
365     /**
366      * Inserts a separator at the specified position.
367      * @param       index the position at which the
368      *                       menu separator should be inserted.
369      * @exception   IllegalArgumentException if the value of
370      *                       <code>index</code> is less than 0.
371      * @see         java.awt.Menu#addSeparator
372      * @since       JDK1.1
373      */
374 
375     public void insertSeparator(int index) {
376         synchronized (getTreeLock()) {
377             if (index < 0) {
378                 throw new IllegalArgumentException("index less than zero.");
379             }
380 
381             int nitems = getItemCount();
382             Vector<MenuItem> tempItems = new Vector<>();
383 
384             /* Remove the item at index, nitems-index times
385                storing them in a temporary vector in the
386                order they appear on the menu.
387             */
388             for (int i = index ; i < nitems; i++) {
389                 tempItems.addElement(getItem(index));
390                 remove(index);
391             }
392 
393             addSeparator();
394 
395             /* Add the removed items back to the menu, they are
396                already in the correct order in the temp vector.
397             */
398             for (int i = 0; i < tempItems.size()  ; i++) {
399                 add(tempItems.elementAt(i));
400             }
401         }
402     }
403 
404     /**
405      * Removes the menu item at the specified index from this menu.
406      * @param       index the position of the item to be removed.
407      */
408     public void remove(int index) {
409         synchronized (getTreeLock()) {
410             MenuItem mi = getItem(index);
411             items.removeElementAt(index);
412             MenuPeer peer = (MenuPeer)this.peer;
413             if (peer != null) {
414                 mi.removeNotify();
415                 mi.parent = null;
416                 peer.delItem(index);
417             }
418         }
419     }
420 
421     /**
422      * Removes the specified menu item from this menu.
423      * @param  item the item to be removed from the menu.
424      *         If <code>item</code> is <code>null</code>
425      *         or is not in this menu, this method does
426      *         nothing.
427      */
428     public void remove(MenuComponent item) {
429         synchronized (getTreeLock()) {
430             int index = items.indexOf(item);
431             if (index >= 0) {
432                 remove(index);
433             }
434         }
435     }
436 
437     /**
438      * Removes all items from this menu.
439      * @since       JDK1.0.
440      */
441     public void removeAll() {
442         synchronized (getTreeLock()) {
443             int nitems = getItemCount();
444             for (int i = nitems-1 ; i >= 0 ; i--) {
445                 remove(i);
446             }
447         }
448     }
449 
450     /*
451      * Post an ActionEvent to the target of the MenuPeer
452      * associated with the specified keyboard event (on
453      * keydown).  Returns true if there is an associated
454      * keyboard event.
455      */
456     boolean handleShortcut(KeyEvent e) {
457         int nitems = getItemCount();
458         for (int i = 0 ; i < nitems ; i++) {
459             MenuItem mi = getItem(i);
460             if (mi.handleShortcut(e)) {
461                 return true;
462             }
463         }
464         return false;
465     }
466 
467     MenuItem getShortcutMenuItem(MenuShortcut s) {
468         int nitems = getItemCount();
469         for (int i = 0 ; i < nitems ; i++) {
470             MenuItem mi = getItem(i).getShortcutMenuItem(s);
471             if (mi != null) {
472                 return mi;
473             }
474         }
475         return null;
476     }
477 
478     synchronized Enumeration<MenuShortcut> shortcuts() {
479         Vector<MenuShortcut> shortcuts = new Vector<>();
480         int nitems = getItemCount();
481         for (int i = 0 ; i < nitems ; i++) {
482             MenuItem mi = getItem(i);
483             if (mi instanceof Menu) {
484                 Enumeration<MenuShortcut> e = ((Menu)mi).shortcuts();
485                 while (e.hasMoreElements()) {
486                     shortcuts.addElement(e.nextElement());
487                 }
488             } else {
489                 MenuShortcut ms = mi.getShortcut();
490                 if (ms != null) {
491                     shortcuts.addElement(ms);
492                 }
493             }
494         }
495         return shortcuts.elements();
496     }
497 
498     void deleteShortcut(MenuShortcut s) {
499         int nitems = getItemCount();
500         for (int i = 0 ; i < nitems ; i++) {
501             getItem(i).deleteShortcut(s);
502         }
503     }
504 
505 
506     /* Serialization support.  A MenuContainer is responsible for
507      * restoring the parent fields of its children.
508      */
509 
510     /**
511      * The menu serialized Data Version.
512      *
513      * @serial
514      */
515     private int menuSerializedDataVersion = 1;
516 
517     /**
518      * Writes default serializable fields to stream.
519      *
520      * @param s the <code>ObjectOutputStream</code> to write
521      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
522      * @see #readObject(ObjectInputStream)
523      */
524     private void writeObject(java.io.ObjectOutputStream s)
525       throws java.io.IOException
526     {
527       s.defaultWriteObject();
528     }
529 
530     /**
531      * Reads the <code>ObjectInputStream</code>.
532      * Unrecognized keys or values will be ignored.
533      *
534      * @param s the <code>ObjectInputStream</code> to read
535      * @exception HeadlessException if
536      *   <code>GraphicsEnvironment.isHeadless</code> returns
537      *   <code>true</code>
538      * @see java.awt.GraphicsEnvironment#isHeadless
539      * @see #writeObject(ObjectOutputStream)
540      */
541     private void readObject(ObjectInputStream s)
542       throws IOException, ClassNotFoundException, HeadlessException
543     {
544       // HeadlessException will be thrown from MenuComponent's readObject
545       s.defaultReadObject();
546       for(int i = 0; i < items.size(); i++) {
547         MenuItem item = (MenuItem)items.elementAt(i);
548         item.parent = this;
549       }
550     }
551 
552     /**
553      * Returns a string representing the state of this <code>Menu</code>.
554      * This method is intended to be used only for debugging purposes, and the
555      * content and format of the returned string may vary between
556      * implementations. The returned string may be empty but may not be
557      * <code>null</code>.
558      *
559      * @return the parameter string of this menu
560      */
561     public String paramString() {
562         String str = ",tearOff=" + tearOff+",isHelpMenu=" + isHelpMenu;
563         return super.paramString() + str;
564     }
565 
566     /**
567      * Initialize JNI field and method IDs
568      */
569     private static native void initIDs();
570 
571 
572 /////////////////
573 // Accessibility support
574 ////////////////
575 
576     /**
577      * Gets the AccessibleContext associated with this Menu.
578      * For menus, the AccessibleContext takes the form of an
579      * AccessibleAWTMenu.
580      * A new AccessibleAWTMenu instance is created if necessary.
581      *
582      * @return an AccessibleAWTMenu that serves as the
583      *         AccessibleContext of this Menu
584      * @since 1.3
585      */
586     public AccessibleContext getAccessibleContext() {
587         if (accessibleContext == null) {
588             accessibleContext = new AccessibleAWTMenu();
589         }
590         return accessibleContext;
591     }
592 
593     /**
594      * Defined in MenuComponent. Overridden here.
595      */
596     int getAccessibleChildIndex(MenuComponent child) {
597         return items.indexOf(child);
598     }
599 
600     /**
601      * Inner class of Menu used to provide default support for
602      * accessibility.  This class is not meant to be used directly by
603      * application developers, but is instead meant only to be
604      * subclassed by menu component developers.
605      * <p>
606      * This class implements accessibility support for the
607      * <code>Menu</code> class.  It provides an implementation of the
608      * Java Accessibility API appropriate to menu user-interface elements.
609      * @since 1.3
610      */
611     protected class AccessibleAWTMenu extends AccessibleAWTMenuItem
612     {
613         /*
614          * JDK 1.3 serialVersionUID
615          */
616         private static final long serialVersionUID = 5228160894980069094L;
617 
618         /**
619          * Get the role of this object.
620          *
621          * @return an instance of AccessibleRole describing the role of the
622          * object
623          */
624         public AccessibleRole getAccessibleRole() {
625             return AccessibleRole.MENU;
626         }
627 
628     } // class AccessibleAWTMenu
629 
630 }