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