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 
  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.
  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  */
  77 public 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 }