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
26package java.awt;
27
28import java.awt.event.KeyEvent;
29import java.awt.peer.MenuBarPeer;
30import java.io.IOException;
31import java.io.ObjectInputStream;
32import java.io.ObjectOutputStream;
33import java.util.Enumeration;
34import java.util.EventListener;
35import java.util.Vector;
36
37import javax.accessibility.Accessible;
38import javax.accessibility.AccessibleContext;
39import javax.accessibility.AccessibleRole;
40
41import 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.
54 * Examples menu is expanded showing items: Basic, Simple, Check, and More Examples."
55 * style="float:center; 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 */
77public class MenuBar extends MenuComponent implements MenuContainer, Accessible {
78
79    static {
80        /* ensure that the necessary native libraries are loaded */
81        Toolkit.loadLibraries();
82        if (!GraphicsEnvironment.isHeadless()) {
83            initIDs();
84        }
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
380    @SuppressWarnings("deprecation")
381    boolean handleShortcut(KeyEvent e) {
382        // Is it a key event?
383        int id = e.getID();
384        if (id != KeyEvent.KEY_PRESSED && id != KeyEvent.KEY_RELEASED) {
385            return false;
386        }
387
388        // Is the accelerator modifier key pressed?
389        int accelKey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
390        if ((e.getModifiers() & accelKey) == 0) {
391            return false;
392        }
393
394        // Pass MenuShortcut on to child menus.
395        int nmenus = getMenuCount();
396        for (int i = 0 ; i < nmenus ; i++) {
397            Menu m = getMenu(i);
398            if (m.handleShortcut(e)) {
399                return true;
400            }
401        }
402        return false;
403    }
404
405    /**
406     * Deletes the specified menu shortcut.
407     * @param     s the menu shortcut to delete.
408     * @since     1.1
409     */
410    public void deleteShortcut(MenuShortcut s) {
411        int nmenus = getMenuCount();
412        for (int i = 0 ; i < nmenus ; i++) {
413            getMenu(i).deleteShortcut(s);
414        }
415    }
416
417    /* Serialization support.  Restore the (transient) parent
418     * fields of Menubar menus here.
419     */
420
421    /**
422     * The MenuBar's serialized data version.
423     *
424     * @serial
425     */
426    private int menuBarSerializedDataVersion = 1;
427
428    /**
429     * Writes default serializable fields to stream.
430     *
431     * @param s the {@code ObjectOutputStream} to write
432     * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
433     * @see #readObject(java.io.ObjectInputStream)
434     */
435    private void writeObject(java.io.ObjectOutputStream s)
436      throws java.lang.ClassNotFoundException,
437             java.io.IOException
438    {
439      s.defaultWriteObject();
440    }
441
442    /**
443     * Reads the {@code ObjectInputStream}.
444     * Unrecognized keys or values will be ignored.
445     *
446     * @param s the {@code ObjectInputStream} to read
447     * @exception HeadlessException if
448     *   {@code GraphicsEnvironment.isHeadless} returns
449     *   {@code true}
450     * @see java.awt.GraphicsEnvironment#isHeadless
451     * @see #writeObject(java.io.ObjectOutputStream)
452     */
453    private void readObject(ObjectInputStream s)
454      throws ClassNotFoundException, IOException, HeadlessException
455    {
456      // HeadlessException will be thrown from MenuComponent's readObject
457      s.defaultReadObject();
458      for (int i = 0; i < menus.size(); i++) {
459        Menu m = menus.elementAt(i);
460        m.parent = this;
461      }
462    }
463
464    /**
465     * Initialize JNI field and method IDs
466     */
467    private static native void initIDs();
468
469
470/////////////////
471// Accessibility support
472////////////////
473
474    /**
475     * Gets the AccessibleContext associated with this MenuBar.
476     * For menu bars, the AccessibleContext takes the form of an
477     * AccessibleAWTMenuBar.
478     * A new AccessibleAWTMenuBar instance is created if necessary.
479     *
480     * @return an AccessibleAWTMenuBar that serves as the
481     *         AccessibleContext of this MenuBar
482     * @since 1.3
483     */
484    public AccessibleContext getAccessibleContext() {
485        if (accessibleContext == null) {
486            accessibleContext = new AccessibleAWTMenuBar();
487        }
488        return accessibleContext;
489    }
490
491    /**
492     * Defined in MenuComponent. Overridden here.
493     */
494    int getAccessibleChildIndex(MenuComponent child) {
495        return menus.indexOf(child);
496    }
497
498    /**
499     * Inner class of MenuBar used to provide default support for
500     * accessibility.  This class is not meant to be used directly by
501     * application developers, but is instead meant only to be
502     * subclassed by menu component developers.
503     * <p>
504     * This class implements accessibility support for the
505     * {@code MenuBar} class.  It provides an implementation of the
506     * Java Accessibility API appropriate to menu bar user-interface elements.
507     * @since 1.3
508     */
509    protected class AccessibleAWTMenuBar extends AccessibleAWTMenuComponent
510    {
511        /*
512         * JDK 1.3 serialVersionUID
513         */
514        private static final long serialVersionUID = -8577604491830083815L;
515
516        /**
517         * Get the role of this object.
518         *
519         * @return an instance of AccessibleRole describing the role of the
520         * object
521         * @since 1.4
522         */
523        public AccessibleRole getAccessibleRole() {
524            return AccessibleRole.MENU_BAR;
525        }
526
527    } // class AccessibleAWTMenuBar
528
529}
530