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