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 sun.awt.AWTAccessor;
32  import java.awt.peer.MenuBarPeer;
33  import java.awt.event.KeyEvent;
34  import javax.accessibility.*;
35  
36  /**
37   * The <code>MenuBar</code> class encapsulates the platform's
38   * concept of a menu bar bound to a frame. In order to associate
39   * the menu bar with a <code>Frame</code> object, call the
40   * frame's <code>setMenuBar</code> method.
41   * <p>
42   * <A NAME="mbexample"></A><!-- target for cross references -->
43   * This is what a menu bar might look like:
44   * <p>
45   * <img src="doc-files/MenuBar-1.gif"
46   * alt="Diagram of MenuBar containing 2 menus: Examples and Options.
47   * Examples menu is expanded showing items: Basic, Simple, Check, and More Examples."
48   * style="float:center; margin: 7px 10px;">
49   * <p>
50   * A menu bar handles keyboard shortcuts for menu items, passing them
51   * along to its child menus.
52   * (Keyboard shortcuts, which are optional, provide the user with
53   * an alternative to the mouse for invoking a menu item and the
54   * action that is associated with it.)
55   * Each menu item can maintain an instance of <code>MenuShortcut</code>.
56   * The <code>MenuBar</code> class defines several methods,
57   * {@link MenuBar#shortcuts} and
58   * {@link MenuBar#getShortcutMenuItem}
59   * that retrieve information about the shortcuts a given
60   * menu bar is managing.
61   *
62   * @author Sami Shaio
63   * @see        java.awt.Frame
64   * @see        java.awt.Frame#setMenuBar(java.awt.MenuBar)
65   * @see        java.awt.Menu
66   * @see        java.awt.MenuItem
67   * @see        java.awt.MenuShortcut
68   * @since      JDK1.0
69   */
70  public class MenuBar extends MenuComponent implements MenuContainer, Accessible {
71  
72      static {
73          /* ensure that the necessary native libraries are loaded */
74          Toolkit.loadLibraries();
75          if (!GraphicsEnvironment.isHeadless()) {
76              initIDs();
77          }
78          AWTAccessor.setMenuBarAccessor(
79              new AWTAccessor.MenuBarAccessor() {
80                  public Menu getHelpMenu(MenuBar menuBar) {
81                      return menuBar.helpMenu;
82                  }
83  
84                  public Vector<Menu> getMenus(MenuBar menuBar) {
85                      return menuBar.menus;
86                  }
87              });
88      }
89  
90      /**
91       * This field represents a vector of the
92       * actual menus that will be part of the MenuBar.
93       *
94       * @serial
95       * @see #countMenus()
96       */
97      Vector<Menu> menus = new Vector<>();
98  
99      /**
100      * This menu is a special menu dedicated to
101      * help.  The one thing to note about this menu
102      * is that on some platforms it appears at the
103      * right edge of the menubar.
104      *
105      * @serial
106      * @see #getHelpMenu()
107      * @see #setHelpMenu(Menu)
108      */
109     Menu helpMenu;
110 
111     private static final String base = "menubar";
112     private static int nameCounter = 0;
113 
114     /*
115      * JDK 1.1 serialVersionUID
116      */
117      private static final long serialVersionUID = -4930327919388951260L;
118 
119     /**
120      * Creates a new menu bar.
121      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
122      * returns true.
123      * @see java.awt.GraphicsEnvironment#isHeadless
124      */
125     public MenuBar() throws HeadlessException {
126     }
127 
128     /**
129      * Construct a name for this MenuComponent.  Called by getName() when
130      * the name is null.
131      */
132     String constructComponentName() {
133         synchronized (MenuBar.class) {
134             return base + nameCounter++;
135         }
136     }
137 
138     /**
139      * Creates the menu bar's peer.  The peer allows us to change the
140      * appearance of the menu bar without changing any of the menu bar's
141      * functionality.
142      */
143     public void addNotify() {
144         synchronized (getTreeLock()) {
145             if (peer == null)
146                 peer = Toolkit.getDefaultToolkit().createMenuBar(this);
147 
148             int nmenus = getMenuCount();
149             for (int i = 0 ; i < nmenus ; i++) {
150                 getMenu(i).addNotify();
151             }
152         }
153     }
154 
155     /**
156      * Removes the menu bar's peer.  The peer allows us to change the
157      * appearance of the menu bar without changing any of the menu bar's
158      * functionality.
159      */
160     public void removeNotify() {
161         synchronized (getTreeLock()) {
162             int nmenus = getMenuCount();
163             for (int i = 0 ; i < nmenus ; i++) {
164                 getMenu(i).removeNotify();
165             }
166             super.removeNotify();
167         }
168     }
169 
170     /**
171      * Gets the help menu on the menu bar.
172      * @return    the help menu on this menu bar.
173      */
174     public Menu getHelpMenu() {
175         return helpMenu;
176     }
177 
178     /**
179      * Sets the specified menu to be this menu bar's help menu.
180      * If this menu bar has an existing help menu, the old help menu is
181      * removed from the menu bar, and replaced with the specified menu.
182      * @param m    the menu to be set as the help menu
183      */
184     public void setHelpMenu(Menu m) {
185         synchronized (getTreeLock()) {
186             if (helpMenu == m) {
187                 return;
188             }
189             if (helpMenu != null) {
190                 remove(helpMenu);
191             }
192             if (m.parent != this) {
193                 add(m);
194             }
195             helpMenu = m;
196             if (m != null) {
197                 m.isHelpMenu = true;
198                 m.parent = this;
199                 MenuBarPeer peer = (MenuBarPeer)this.peer;
200                 if (peer != null) {
201                     if (m.peer == null) {
202                         m.addNotify();
203                     }
204                     peer.addHelpMenu(m);
205                 }
206             }
207         }
208     }
209 
210     /**
211      * Adds the specified menu to the menu bar.
212      * If the menu has been part of another menu bar,
213      * removes it from that menu bar.
214      *
215      * @param        m   the menu to be added
216      * @return       the menu added
217      * @see          java.awt.MenuBar#remove(int)
218      * @see          java.awt.MenuBar#remove(java.awt.MenuComponent)
219      */
220     public Menu add(Menu m) {
221         synchronized (getTreeLock()) {
222             if (m.parent != null) {
223                 m.parent.remove(m);
224             }
225             menus.addElement(m);
226             m.parent = this;
227 
228             MenuBarPeer peer = (MenuBarPeer)this.peer;
229             if (peer != null) {
230                 if (m.peer == null) {
231                     m.addNotify();
232                 }
233                 peer.addMenu(m);
234             }
235             return m;
236         }
237     }
238 
239     /**
240      * Removes the menu located at the specified
241      * index from this menu bar.
242      * @param        index   the position of the menu to be removed.
243      * @see          java.awt.MenuBar#add(java.awt.Menu)
244      */
245     public void remove(int index) {
246         synchronized (getTreeLock()) {
247             Menu m = getMenu(index);
248             menus.removeElementAt(index);
249             MenuBarPeer peer = (MenuBarPeer)this.peer;
250             if (peer != null) {
251                 m.removeNotify();
252                 m.parent = null;
253                 peer.delMenu(index);
254             }
255         }
256     }
257 
258     /**
259      * Removes the specified menu component from this menu bar.
260      * @param        m the menu component to be removed.
261      * @see          java.awt.MenuBar#add(java.awt.Menu)
262      */
263     public void remove(MenuComponent m) {
264         synchronized (getTreeLock()) {
265             int index = menus.indexOf(m);
266             if (index >= 0) {
267                 remove(index);
268             }
269         }
270     }
271 
272     /**
273      * Gets the number of menus on the menu bar.
274      * @return     the number of menus on the menu bar.
275      * @since      JDK1.1
276      */
277     public int getMenuCount() {
278         return countMenus();
279     }
280 
281     /**
282      * @deprecated As of JDK version 1.1,
283      * replaced by <code>getMenuCount()</code>.
284      */
285     @Deprecated
286     public int countMenus() {
287         return getMenuCountImpl();
288     }
289 
290     /*
291      * This is called by the native code, so client code can't
292      * be called on the toolkit thread.
293      */
294     final int getMenuCountImpl() {
295         return menus.size();
296     }
297 
298     /**
299      * Gets the specified menu.
300      * @param      i the index position of the menu to be returned.
301      * @return     the menu at the specified index of this menu bar.
302      */
303     public Menu getMenu(int i) {
304         return getMenuImpl(i);
305     }
306 
307     /*
308      * This is called by the native code, so client code can't
309      * be called on the toolkit thread.
310      */
311     final Menu getMenuImpl(int i) {
312         return menus.elementAt(i);
313     }
314 
315     /**
316      * Gets an enumeration of all menu shortcuts this menu bar
317      * is managing.
318      * @return      an enumeration of menu shortcuts that this
319      *                      menu bar is managing.
320      * @see         java.awt.MenuShortcut
321      * @since       JDK1.1
322      */
323     public synchronized Enumeration<MenuShortcut> shortcuts() {
324         Vector<MenuShortcut> shortcuts = new Vector<>();
325         int nmenus = getMenuCount();
326         for (int i = 0 ; i < nmenus ; i++) {
327             Enumeration<MenuShortcut> e = getMenu(i).shortcuts();
328             while (e.hasMoreElements()) {
329                 shortcuts.addElement(e.nextElement());
330             }
331         }
332         return shortcuts.elements();
333     }
334 
335     /**
336      * Gets the instance of <code>MenuItem</code> associated
337      * with the specified <code>MenuShortcut</code> object,
338      * or <code>null</code> if none of the menu items being managed
339      * by this menu bar is associated with the specified menu
340      * shortcut.
341      * @param        s the specified menu shortcut.
342      * @see          java.awt.MenuItem
343      * @see          java.awt.MenuShortcut
344      * @since        JDK1.1
345      */
346      public MenuItem getShortcutMenuItem(MenuShortcut s) {
347         int nmenus = getMenuCount();
348         for (int i = 0 ; i < nmenus ; i++) {
349             MenuItem mi = getMenu(i).getShortcutMenuItem(s);
350             if (mi != null) {
351                 return mi;
352             }
353         }
354         return null;  // MenuShortcut wasn't found
355      }
356 
357     /*
358      * Post an ACTION_EVENT to the target of the MenuPeer
359      * associated with the specified keyboard event (on
360      * keydown).  Returns true if there is an associated
361      * keyboard event.
362      */
363     boolean handleShortcut(KeyEvent e) {
364         // Is it a key event?
365         int id = e.getID();
366         if (id != KeyEvent.KEY_PRESSED && id != KeyEvent.KEY_RELEASED) {
367             return false;
368         }
369 
370         // Is the accelerator modifier key pressed?
371         int accelKey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
372         if ((e.getModifiers() & accelKey) == 0) {
373             return false;
374         }
375 
376         // Pass MenuShortcut on to child menus.
377         int nmenus = getMenuCount();
378         for (int i = 0 ; i < nmenus ; i++) {
379             Menu m = getMenu(i);
380             if (m.handleShortcut(e)) {
381                 return true;
382             }
383         }
384         return false;
385     }
386 
387     /**
388      * Deletes the specified menu shortcut.
389      * @param     s the menu shortcut to delete.
390      * @since     JDK1.1
391      */
392     public void deleteShortcut(MenuShortcut s) {
393         int nmenus = getMenuCount();
394         for (int i = 0 ; i < nmenus ; i++) {
395             getMenu(i).deleteShortcut(s);
396         }
397     }
398 
399     /* Serialization support.  Restore the (transient) parent
400      * fields of Menubar menus here.
401      */
402 
403     /**
404      * The MenuBar's serialized data version.
405      *
406      * @serial
407      */
408     private int menuBarSerializedDataVersion = 1;
409 
410     /**
411      * Writes default serializable fields to stream.
412      *
413      * @param s the <code>ObjectOutputStream</code> to write
414      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
415      * @see #readObject(java.io.ObjectInputStream)
416      */
417     private void writeObject(java.io.ObjectOutputStream s)
418       throws java.lang.ClassNotFoundException,
419              java.io.IOException
420     {
421       s.defaultWriteObject();
422     }
423 
424     /**
425      * Reads the <code>ObjectInputStream</code>.
426      * Unrecognized keys or values will be ignored.
427      *
428      * @param s the <code>ObjectInputStream</code> to read
429      * @exception HeadlessException if
430      *   <code>GraphicsEnvironment.isHeadless</code> returns
431      *   <code>true</code>
432      * @see java.awt.GraphicsEnvironment#isHeadless
433      * @see #writeObject(java.io.ObjectOutputStream)
434      */
435     private void readObject(ObjectInputStream s)
436       throws ClassNotFoundException, IOException, HeadlessException
437     {
438       // HeadlessException will be thrown from MenuComponent's readObject
439       s.defaultReadObject();
440       for (int i = 0; i < menus.size(); i++) {
441         Menu m = menus.elementAt(i);
442         m.parent = this;
443       }
444     }
445 
446     /**
447      * Initialize JNI field and method IDs
448      */
449     private static native void initIDs();
450 
451 
452 /////////////////
453 // Accessibility support
454 ////////////////
455 
456     /**
457      * Gets the AccessibleContext associated with this MenuBar.
458      * For menu bars, the AccessibleContext takes the form of an
459      * AccessibleAWTMenuBar.
460      * A new AccessibleAWTMenuBar instance is created if necessary.
461      *
462      * @return an AccessibleAWTMenuBar that serves as the
463      *         AccessibleContext of this MenuBar
464      * @since 1.3
465      */
466     public AccessibleContext getAccessibleContext() {
467         if (accessibleContext == null) {
468             accessibleContext = new AccessibleAWTMenuBar();
469         }
470         return accessibleContext;
471     }
472 
473     /**
474      * Defined in MenuComponent. Overridden here.
475      */
476     int getAccessibleChildIndex(MenuComponent child) {
477         return menus.indexOf(child);
478     }
479 
480     /**
481      * Inner class of MenuBar used to provide default support for
482      * accessibility.  This class is not meant to be used directly by
483      * application developers, but is instead meant only to be
484      * subclassed by menu component developers.
485      * <p>
486      * This class implements accessibility support for the
487      * <code>MenuBar</code> class.  It provides an implementation of the
488      * Java Accessibility API appropriate to menu bar user-interface elements.
489      * @since 1.3
490      */
491     protected class AccessibleAWTMenuBar extends AccessibleAWTMenuComponent
492     {
493         /*
494          * JDK 1.3 serialVersionUID
495          */
496         private static final long serialVersionUID = -8577604491830083815L;
497 
498         /**
499          * Get the role of this object.
500          *
501          * @return an instance of AccessibleRole describing the role of the
502          * object
503          * @since 1.4
504          */
505         public AccessibleRole getAccessibleRole() {
506             return AccessibleRole.MENU_BAR;
507         }
508 
509     } // class AccessibleAWTMenuBar
510 
511 }