1 /*
2  * Copyright (c) 1995, 2015, 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 */
Toolkit.loadLibraries()74         Toolkit.loadLibraries();
75         if (!GraphicsEnvironment.isHeadless()) {
initIDs()76             initIDs();
77         }
AWTAccessor.setMenuBarAccessor( new AWTAccessor.MenuBarAccessor() { public Menu getHelpMenu(MenuBar menuBar) { return menuBar.helpMenu; } public Vector<Menu> getMenus(MenuBar menuBar) { return menuBar.menus; } })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      */
MenuBar()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      */
constructComponentName()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      */
addNotify()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      */
removeNotify()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      */
getHelpMenu()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      */
setHelpMenu(final Menu m)184     public void setHelpMenu(final Menu m) {
185         synchronized (getTreeLock()) {
186             if (helpMenu == m) {
187                 return;
188             }
189             if (helpMenu != null) {
190                 remove(helpMenu);
191             }
192             helpMenu = m;
193             if (m != null) {
194                 if (m.parent != this) {
195                     add(m);
196                 }
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      */
add(Menu m)220     public Menu add(Menu m) {
221         synchronized (getTreeLock()) {
222             if (m.parent != null) {
223                 m.parent.remove(m);
224             }
225             m.parent = this;
226 
227             MenuBarPeer peer = (MenuBarPeer)this.peer;
228             if (peer != null) {
229                 if (m.peer == null) {
230                     m.addNotify();
231                 }
232                 menus.addElement(m);
233                 peer.addMenu(m);
234             } else {
235                 menus.addElement(m);
236             }
237             return m;
238         }
239     }
240 
241     /**
242      * Removes the menu located at the specified
243      * index from this menu bar.
244      * @param        index   the position of the menu to be removed.
245      * @see          java.awt.MenuBar#add(java.awt.Menu)
246      */
remove(final int index)247     public void remove(final int index) {
248         synchronized (getTreeLock()) {
249             Menu m = getMenu(index);
250             menus.removeElementAt(index);
251             MenuBarPeer peer = (MenuBarPeer)this.peer;
252             if (peer != null) {
253                 peer.delMenu(index);
254                 m.removeNotify();
255                 m.parent = null;
256             }
257             if (helpMenu == m) {
258                 helpMenu = null;
259                 m.isHelpMenu = false;
260             }
261         }
262     }
263 
264     /**
265      * Removes the specified menu component from this menu bar.
266      * @param        m the menu component to be removed.
267      * @see          java.awt.MenuBar#add(java.awt.Menu)
268      */
remove(MenuComponent m)269     public void remove(MenuComponent m) {
270         synchronized (getTreeLock()) {
271             int index = menus.indexOf(m);
272             if (index >= 0) {
273                 remove(index);
274             }
275         }
276     }
277 
278     /**
279      * Gets the number of menus on the menu bar.
280      * @return     the number of menus on the menu bar.
281      * @since      JDK1.1
282      */
getMenuCount()283     public int getMenuCount() {
284         return countMenus();
285     }
286 
287     /**
288      * @deprecated As of JDK version 1.1,
289      * replaced by <code>getMenuCount()</code>.
290      */
291     @Deprecated
countMenus()292     public int countMenus() {
293         return getMenuCountImpl();
294     }
295 
296     /*
297      * This is called by the native code, so client code can't
298      * be called on the toolkit thread.
299      */
getMenuCountImpl()300     final int getMenuCountImpl() {
301         return menus.size();
302     }
303 
304     /**
305      * Gets the specified menu.
306      * @param      i the index position of the menu to be returned.
307      * @return     the menu at the specified index of this menu bar.
308      */
getMenu(int i)309     public Menu getMenu(int i) {
310         return getMenuImpl(i);
311     }
312 
313     /*
314      * This is called by the native code, so client code can't
315      * be called on the toolkit thread.
316      */
getMenuImpl(int i)317     final Menu getMenuImpl(int i) {
318         return menus.elementAt(i);
319     }
320 
321     /**
322      * Gets an enumeration of all menu shortcuts this menu bar
323      * is managing.
324      * @return      an enumeration of menu shortcuts that this
325      *                      menu bar is managing.
326      * @see         java.awt.MenuShortcut
327      * @since       JDK1.1
328      */
shortcuts()329     public synchronized Enumeration<MenuShortcut> shortcuts() {
330         Vector<MenuShortcut> shortcuts = new Vector<>();
331         int nmenus = getMenuCount();
332         for (int i = 0 ; i < nmenus ; i++) {
333             Enumeration<MenuShortcut> e = getMenu(i).shortcuts();
334             while (e.hasMoreElements()) {
335                 shortcuts.addElement(e.nextElement());
336             }
337         }
338         return shortcuts.elements();
339     }
340 
341     /**
342      * Gets the instance of <code>MenuItem</code> associated
343      * with the specified <code>MenuShortcut</code> object,
344      * or <code>null</code> if none of the menu items being managed
345      * by this menu bar is associated with the specified menu
346      * shortcut.
347      * @param        s the specified menu shortcut.
348      * @see          java.awt.MenuItem
349      * @see          java.awt.MenuShortcut
350      * @since        JDK1.1
351      */
getShortcutMenuItem(MenuShortcut s)352      public MenuItem getShortcutMenuItem(MenuShortcut s) {
353         int nmenus = getMenuCount();
354         for (int i = 0 ; i < nmenus ; i++) {
355             MenuItem mi = getMenu(i).getShortcutMenuItem(s);
356             if (mi != null) {
357                 return mi;
358             }
359         }
360         return null;  // MenuShortcut wasn't found
361      }
362 
363     /*
364      * Post an ACTION_EVENT to the target of the MenuPeer
365      * associated with the specified keyboard event (on
366      * keydown).  Returns true if there is an associated
367      * keyboard event.
368      */
handleShortcut(KeyEvent e)369     boolean handleShortcut(KeyEvent e) {
370         // Is it a key event?
371         int id = e.getID();
372         if (id != KeyEvent.KEY_PRESSED && id != KeyEvent.KEY_RELEASED) {
373             return false;
374         }
375 
376         // Is the accelerator modifier key pressed?
377         int accelKey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
378         if ((e.getModifiers() & accelKey) == 0) {
379             return false;
380         }
381 
382         // Pass MenuShortcut on to child menus.
383         int nmenus = getMenuCount();
384         for (int i = 0 ; i < nmenus ; i++) {
385             Menu m = getMenu(i);
386             if (m.handleShortcut(e)) {
387                 return true;
388             }
389         }
390         return false;
391     }
392 
393     /**
394      * Deletes the specified menu shortcut.
395      * @param     s the menu shortcut to delete.
396      * @since     JDK1.1
397      */
deleteShortcut(MenuShortcut s)398     public void deleteShortcut(MenuShortcut s) {
399         int nmenus = getMenuCount();
400         for (int i = 0 ; i < nmenus ; i++) {
401             getMenu(i).deleteShortcut(s);
402         }
403     }
404 
405     /* Serialization support.  Restore the (transient) parent
406      * fields of Menubar menus here.
407      */
408 
409     /**
410      * The MenuBar's serialized data version.
411      *
412      * @serial
413      */
414     private int menuBarSerializedDataVersion = 1;
415 
416     /**
417      * Writes default serializable fields to stream.
418      *
419      * @param s the <code>ObjectOutputStream</code> to write
420      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
421      * @see #readObject(java.io.ObjectInputStream)
422      */
writeObject(java.io.ObjectOutputStream s)423     private void writeObject(java.io.ObjectOutputStream s)
424       throws java.lang.ClassNotFoundException,
425              java.io.IOException
426     {
427       s.defaultWriteObject();
428     }
429 
430     /**
431      * Reads the <code>ObjectInputStream</code>.
432      * Unrecognized keys or values will be ignored.
433      *
434      * @param s the <code>ObjectInputStream</code> to read
435      * @exception HeadlessException if
436      *   <code>GraphicsEnvironment.isHeadless</code> returns
437      *   <code>true</code>
438      * @see java.awt.GraphicsEnvironment#isHeadless
439      * @see #writeObject(java.io.ObjectOutputStream)
440      */
readObject(ObjectInputStream s)441     private void readObject(ObjectInputStream s)
442       throws ClassNotFoundException, IOException, HeadlessException
443     {
444       // HeadlessException will be thrown from MenuComponent's readObject
445       s.defaultReadObject();
446       for (int i = 0; i < menus.size(); i++) {
447         Menu m = menus.elementAt(i);
448         m.parent = this;
449       }
450     }
451 
452     /**
453      * Initialize JNI field and method IDs
454      */
initIDs()455     private static native void initIDs();
456 
457 
458 /////////////////
459 // Accessibility support
460 ////////////////
461 
462     /**
463      * Gets the AccessibleContext associated with this MenuBar.
464      * For menu bars, the AccessibleContext takes the form of an
465      * AccessibleAWTMenuBar.
466      * A new AccessibleAWTMenuBar instance is created if necessary.
467      *
468      * @return an AccessibleAWTMenuBar that serves as the
469      *         AccessibleContext of this MenuBar
470      * @since 1.3
471      */
getAccessibleContext()472     public AccessibleContext getAccessibleContext() {
473         if (accessibleContext == null) {
474             accessibleContext = new AccessibleAWTMenuBar();
475         }
476         return accessibleContext;
477     }
478 
479     /**
480      * Defined in MenuComponent. Overridden here.
481      */
getAccessibleChildIndex(MenuComponent child)482     int getAccessibleChildIndex(MenuComponent child) {
483         return menus.indexOf(child);
484     }
485 
486     /**
487      * Inner class of MenuBar used to provide default support for
488      * accessibility.  This class is not meant to be used directly by
489      * application developers, but is instead meant only to be
490      * subclassed by menu component developers.
491      * <p>
492      * This class implements accessibility support for the
493      * <code>MenuBar</code> class.  It provides an implementation of the
494      * Java Accessibility API appropriate to menu bar user-interface elements.
495      * @since 1.3
496      */
497     protected class AccessibleAWTMenuBar extends AccessibleAWTMenuComponent
498     {
499         /*
500          * JDK 1.3 serialVersionUID
501          */
502         private static final long serialVersionUID = -8577604491830083815L;
503 
504         /**
505          * Get the role of this object.
506          *
507          * @return an instance of AccessibleRole describing the role of the
508          * object
509          * @since 1.4
510          */
getAccessibleRole()511         public AccessibleRole getAccessibleRole() {
512             return AccessibleRole.MENU_BAR;
513         }
514 
515     } // class AccessibleAWTMenuBar
516 
517 }
518