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