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.MenuPeer; 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 * A {@code Menu} object is a pull-down menu component 45 * that is deployed from a menu bar. 46 * <p> 47 * A menu can optionally be a <i>tear-off</i> menu. A tear-off menu 48 * can be opened and dragged away from its parent menu bar or menu. 49 * It remains on the screen after the mouse button has been released. 50 * The mechanism for tearing off a menu is platform dependent, since 51 * the look and feel of the tear-off menu is determined by its peer. 52 * On platforms that do not support tear-off menus, the tear-off 53 * property is ignored. 54 * <p> 55 * Each item in a menu must belong to the {@code MenuItem} 56 * class. It can be an instance of {@code MenuItem}, a submenu 57 * (an instance of {@code Menu}), or a check box (an instance of 58 * {@code CheckboxMenuItem}). 59 * 60 * @author Sami Shaio 61 * @see java.awt.MenuItem 62 * @see java.awt.CheckboxMenuItem 63 * @since 1.0 64 */ 65public class Menu extends MenuItem implements MenuContainer, Accessible { 66 67 static { 68 /* ensure that the necessary native libraries are loaded */ 69 Toolkit.loadLibraries(); 70 if (!GraphicsEnvironment.isHeadless()) { 71 initIDs(); 72 } 73 74 AWTAccessor.setMenuAccessor( 75 new AWTAccessor.MenuAccessor() { 76 public Vector<MenuItem> getItems(Menu menu) { 77 return menu.items; 78 } 79 }); 80 } 81 82 /** 83 * A vector of the items that will be part of the Menu. 84 * 85 * @serial 86 * @see #countItems() 87 */ 88 private final Vector<MenuItem> items = new Vector<>(); 89 90 /** 91 * This field indicates whether the menu has the 92 * tear of property or not. It will be set to 93 * {@code true} if the menu has the tear off 94 * property and it will be set to {@code false} 95 * if it does not. 96 * A torn off menu can be deleted by a user when 97 * it is no longer needed. 98 * 99 * @serial 100 * @see #isTearOff() 101 */ 102 private final boolean tearOff; 103 104 /** 105 * This field will be set to {@code true} 106 * if the Menu in question is actually a help 107 * menu. Otherwise it will be set to 108 * {@code false}. 109 * 110 * @serial 111 */ 112 volatile boolean isHelpMenu; 113 114 private static final String base = "menu"; 115 private static int nameCounter = 0; 116 117 /* 118 * JDK 1.1 serialVersionUID 119 */ 120 private static final long serialVersionUID = -8809584163345499784L; 121 122 /** 123 * Constructs a new menu with an empty label. This menu is not 124 * a tear-off menu. 125 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 126 * returns true. 127 * @see java.awt.GraphicsEnvironment#isHeadless 128 * @since 1.1 129 */ 130 public Menu() throws HeadlessException { 131 this("", false); 132 } 133 134 /** 135 * Constructs a new menu with the specified label. This menu is not 136 * a tear-off menu. 137 * @param label the menu's label in the menu bar, or in 138 * another menu of which this menu is a submenu. 139 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 140 * returns true. 141 * @see java.awt.GraphicsEnvironment#isHeadless 142 */ 143 public Menu(String label) throws HeadlessException { 144 this(label, false); 145 } 146 147 /** 148 * Constructs a new menu with the specified label, 149 * indicating whether the menu can be torn off. 150 * <p> 151 * Tear-off functionality may not be supported by all 152 * implementations of AWT. If a particular implementation doesn't 153 * support tear-off menus, this value is silently ignored. 154 * @param label the menu's label in the menu bar, or in 155 * another menu of which this menu is a submenu. 156 * @param tearOff if {@code true}, the menu 157 * is a tear-off menu. 158 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 159 * returns true. 160 * @see java.awt.GraphicsEnvironment#isHeadless 161 */ 162 public Menu(String label, boolean tearOff) throws HeadlessException { 163 super(label); 164 this.tearOff = tearOff; 165 } 166 167 /** 168 * Construct a name for this MenuComponent. Called by getName() when 169 * the name is null. 170 */ 171 String constructComponentName() { 172 synchronized (Menu.class) { 173 return base + nameCounter++; 174 } 175 } 176 177 /** 178 * Creates the menu's peer. The peer allows us to modify the 179 * appearance of the menu without changing its functionality. 180 */ 181 public void addNotify() { 182 synchronized (getTreeLock()) { 183 if (peer == null) 184 peer = getComponentFactory().createMenu(this); 185 int nitems = getItemCount(); 186 for (int i = 0 ; i < nitems ; i++) { 187 MenuItem mi = getItem(i); 188 mi.parent = this; 189 mi.addNotify(); 190 } 191 } 192 } 193 194 /** 195 * Removes the menu's peer. The peer allows us to modify the appearance 196 * of the menu without changing its functionality. 197 */ 198 public void removeNotify() { 199 synchronized (getTreeLock()) { 200 int nitems = getItemCount(); 201 for (int i = 0 ; i < nitems ; i++) { 202 getItem(i).removeNotify(); 203 } 204 super.removeNotify(); 205 } 206 } 207 208 /** 209 * Indicates whether this menu is a tear-off menu. 210 * <p> 211 * Tear-off functionality may not be supported by all 212 * implementations of AWT. If a particular implementation doesn't 213 * support tear-off menus, this value is silently ignored. 214 * @return {@code true} if this is a tear-off menu; 215 * {@code false} otherwise. 216 */ 217 public boolean isTearOff() { 218 return tearOff; 219 } 220 221 /** 222 * Get the number of items in this menu. 223 * @return the number of items in this menu 224 * @since 1.1 225 */ 226 public int getItemCount() { 227 return countItems(); 228 } 229 230 /** 231 * Returns the number of items in this menu. 232 * 233 * @return the number of items in this menu 234 * @deprecated As of JDK version 1.1, 235 * replaced by {@code getItemCount()}. 236 */ 237 @Deprecated 238 public int countItems() { 239 return countItemsImpl(); 240 } 241 242 /* 243 * This is called by the native code, so client code can't 244 * be called on the toolkit thread. 245 */ 246 final int countItemsImpl() { 247 return items.size(); 248 } 249 250 /** 251 * Gets the item located at the specified index of this menu. 252 * @param index the position of the item to be returned. 253 * @return the item located at the specified index. 254 */ 255 public MenuItem getItem(int index) { 256 return getItemImpl(index); 257 } 258 259 /* 260 * This is called by the native code, so client code can't 261 * be called on the toolkit thread. 262 */ 263 final MenuItem getItemImpl(int index) { 264 return items.elementAt(index); 265 } 266 267 /** 268 * Adds the specified menu item to this menu. If the 269 * menu item has been part of another menu, removes it 270 * from that menu. 271 * 272 * @param mi the menu item to be added 273 * @return the menu item added 274 * @see java.awt.Menu#insert(java.lang.String, int) 275 * @see java.awt.Menu#insert(java.awt.MenuItem, int) 276 */ 277 public MenuItem add(MenuItem mi) { 278 synchronized (getTreeLock()) { 279 if (mi.parent != null) { 280 mi.parent.remove(mi); 281 } 282 items.addElement(mi); 283 mi.parent = this; 284 MenuPeer peer = (MenuPeer)this.peer; 285 if (peer != null) { 286 mi.addNotify(); 287 peer.addItem(mi); 288 } 289 return mi; 290 } 291 } 292 293 /** 294 * Adds an item with the specified label to this menu. 295 * 296 * @param label the text on the item 297 * @see java.awt.Menu#insert(java.lang.String, int) 298 * @see java.awt.Menu#insert(java.awt.MenuItem, int) 299 */ 300 public void add(String label) { 301 add(new MenuItem(label)); 302 } 303 304 /** 305 * Inserts a menu item into this menu 306 * at the specified position. 307 * 308 * @param menuitem the menu item to be inserted. 309 * @param index the position at which the menu 310 * item should be inserted. 311 * @see java.awt.Menu#add(java.lang.String) 312 * @see java.awt.Menu#add(java.awt.MenuItem) 313 * @exception IllegalArgumentException if the value of 314 * {@code index} is less than zero 315 * @since 1.1 316 */ 317 318 public void insert(MenuItem menuitem, int index) { 319 synchronized (getTreeLock()) { 320 if (index < 0) { 321 throw new IllegalArgumentException("index less than zero."); 322 } 323 324 int nitems = getItemCount(); 325 Vector<MenuItem> tempItems = new Vector<>(); 326 327 /* Remove the item at index, nitems-index times 328 storing them in a temporary vector in the 329 order they appear on the menu. 330 */ 331 for (int i = index ; i < nitems; i++) { 332 tempItems.addElement(getItem(index)); 333 remove(index); 334 } 335 336 add(menuitem); 337 338 /* Add the removed items back to the menu, they are 339 already in the correct order in the temp vector. 340 */ 341 for (int i = 0; i < tempItems.size() ; i++) { 342 add(tempItems.elementAt(i)); 343 } 344 } 345 } 346 347 /** 348 * Inserts a menu item with the specified label into this menu 349 * at the specified position. This is a convenience method for 350 * {@code insert(menuItem, index)}. 351 * 352 * @param label the text on the item 353 * @param index the position at which the menu item 354 * should be inserted 355 * @see java.awt.Menu#add(java.lang.String) 356 * @see java.awt.Menu#add(java.awt.MenuItem) 357 * @exception IllegalArgumentException if the value of 358 * {@code index} is less than zero 359 * @since 1.1 360 */ 361 362 public void insert(String label, int index) { 363 insert(new MenuItem(label), index); 364 } 365 366 /** 367 * Adds a separator line, or a hypen, to the menu at the current position. 368 * @see java.awt.Menu#insertSeparator(int) 369 */ 370 public void addSeparator() { 371 add("-"); 372 } 373 374 /** 375 * Inserts a separator at the specified position. 376 * @param index the position at which the 377 * menu separator should be inserted. 378 * @exception IllegalArgumentException if the value of 379 * {@code index} is less than 0. 380 * @see java.awt.Menu#addSeparator 381 * @since 1.1 382 */ 383 384 public void insertSeparator(int index) { 385 synchronized (getTreeLock()) { 386 if (index < 0) { 387 throw new IllegalArgumentException("index less than zero."); 388 } 389 390 int nitems = getItemCount(); 391 Vector<MenuItem> tempItems = new Vector<>(); 392 393 /* Remove the item at index, nitems-index times 394 storing them in a temporary vector in the 395 order they appear on the menu. 396 */ 397 for (int i = index ; i < nitems; i++) { 398 tempItems.addElement(getItem(index)); 399 remove(index); 400 } 401 402 addSeparator(); 403 404 /* Add the removed items back to the menu, they are 405 already in the correct order in the temp vector. 406 */ 407 for (int i = 0; i < tempItems.size() ; i++) { 408 add(tempItems.elementAt(i)); 409 } 410 } 411 } 412 413 /** 414 * Removes the menu item at the specified index from this menu. 415 * @param index the position of the item to be removed. 416 */ 417 public void remove(int index) { 418 synchronized (getTreeLock()) { 419 MenuItem mi = getItem(index); 420 items.removeElementAt(index); 421 MenuPeer peer = (MenuPeer)this.peer; 422 if (peer != null) { 423 peer.delItem(index); 424 mi.removeNotify(); 425 } 426 mi.parent = null; 427 } 428 } 429 430 /** 431 * Removes the specified menu item from this menu. 432 * @param item the item to be removed from the menu. 433 * If {@code item} is {@code null} 434 * or is not in this menu, this method does 435 * nothing. 436 */ 437 public void remove(MenuComponent item) { 438 synchronized (getTreeLock()) { 439 int index = items.indexOf(item); 440 if (index >= 0) { 441 remove(index); 442 } 443 } 444 } 445 446 /** 447 * Removes all items from this menu. 448 * @since 1.1 449 */ 450 public void removeAll() { 451 synchronized (getTreeLock()) { 452 int nitems = getItemCount(); 453 for (int i = nitems-1 ; i >= 0 ; i--) { 454 remove(i); 455 } 456 } 457 } 458 459 /* 460 * Post an ActionEvent to the target of the MenuPeer 461 * associated with the specified keyboard event (on 462 * keydown). Returns true if there is an associated 463 * keyboard event. 464 */ 465 boolean handleShortcut(KeyEvent e) { 466 int nitems = getItemCount(); 467 for (int i = 0 ; i < nitems ; i++) { 468 MenuItem mi = getItem(i); 469 if (mi.handleShortcut(e)) { 470 return true; 471 } 472 } 473 return false; 474 } 475 476 MenuItem getShortcutMenuItem(MenuShortcut s) { 477 int nitems = getItemCount(); 478 for (int i = 0 ; i < nitems ; i++) { 479 MenuItem mi = getItem(i).getShortcutMenuItem(s); 480 if (mi != null) { 481 return mi; 482 } 483 } 484 return null; 485 } 486 487 synchronized Enumeration<MenuShortcut> shortcuts() { 488 Vector<MenuShortcut> shortcuts = new Vector<>(); 489 int nitems = getItemCount(); 490 for (int i = 0 ; i < nitems ; i++) { 491 MenuItem mi = getItem(i); 492 if (mi instanceof Menu) { 493 Enumeration<MenuShortcut> e = ((Menu)mi).shortcuts(); 494 while (e.hasMoreElements()) { 495 shortcuts.addElement(e.nextElement()); 496 } 497 } else { 498 MenuShortcut ms = mi.getShortcut(); 499 if (ms != null) { 500 shortcuts.addElement(ms); 501 } 502 } 503 } 504 return shortcuts.elements(); 505 } 506 507 void deleteShortcut(MenuShortcut s) { 508 int nitems = getItemCount(); 509 for (int i = 0 ; i < nitems ; i++) { 510 getItem(i).deleteShortcut(s); 511 } 512 } 513 514 515 /* Serialization support. A MenuContainer is responsible for 516 * restoring the parent fields of its children. 517 */ 518 519 /** 520 * The menu serialized Data Version. 521 * 522 * @serial 523 */ 524 private int menuSerializedDataVersion = 1; 525 526 /** 527 * Writes default serializable fields to stream. 528 * 529 * @param s the {@code ObjectOutputStream} to write 530 * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener) 531 * @see #readObject(ObjectInputStream) 532 */ 533 private void writeObject(java.io.ObjectOutputStream s) 534 throws java.io.IOException 535 { 536 s.defaultWriteObject(); 537 } 538 539 /** 540 * Reads the {@code ObjectInputStream}. 541 * Unrecognized keys or values will be ignored. 542 * 543 * @param s the {@code ObjectInputStream} to read 544 * @exception HeadlessException if 545 * {@code GraphicsEnvironment.isHeadless} returns 546 * {@code true} 547 * @see java.awt.GraphicsEnvironment#isHeadless 548 * @see #writeObject(ObjectOutputStream) 549 */ 550 private void readObject(ObjectInputStream s) 551 throws IOException, ClassNotFoundException, HeadlessException 552 { 553 // HeadlessException will be thrown from MenuComponent's readObject 554 s.defaultReadObject(); 555 for(int i = 0; i < items.size(); i++) { 556 MenuItem item = items.elementAt(i); 557 item.parent = this; 558 } 559 } 560 561 /** 562 * Returns a string representing the state of this {@code Menu}. 563 * This method is intended to be used only for debugging purposes, and the 564 * content and format of the returned string may vary between 565 * implementations. The returned string may be empty but may not be 566 * {@code null}. 567 * 568 * @return the parameter string of this menu 569 */ 570 public String paramString() { 571 String str = ",tearOff=" + tearOff+",isHelpMenu=" + isHelpMenu; 572 return super.paramString() + str; 573 } 574 575 /** 576 * Initialize JNI field and method IDs 577 */ 578 private static native void initIDs(); 579 580 581///////////////// 582// Accessibility support 583//////////////// 584 585 /** 586 * Gets the AccessibleContext associated with this Menu. 587 * For menus, the AccessibleContext takes the form of an 588 * AccessibleAWTMenu. 589 * A new AccessibleAWTMenu instance is created if necessary. 590 * 591 * @return an AccessibleAWTMenu that serves as the 592 * AccessibleContext of this Menu 593 * @since 1.3 594 */ 595 public AccessibleContext getAccessibleContext() { 596 if (accessibleContext == null) { 597 accessibleContext = new AccessibleAWTMenu(); 598 } 599 return accessibleContext; 600 } 601 602 /** 603 * Defined in MenuComponent. Overridden here. 604 */ 605 int getAccessibleChildIndex(MenuComponent child) { 606 return items.indexOf(child); 607 } 608 609 /** 610 * Inner class of Menu used to provide default support for 611 * accessibility. This class is not meant to be used directly by 612 * application developers, but is instead meant only to be 613 * subclassed by menu component developers. 614 * <p> 615 * This class implements accessibility support for the 616 * {@code Menu} class. It provides an implementation of the 617 * Java Accessibility API appropriate to menu user-interface elements. 618 * @since 1.3 619 */ 620 protected class AccessibleAWTMenu extends AccessibleAWTMenuItem 621 { 622 /* 623 * JDK 1.3 serialVersionUID 624 */ 625 private static final long serialVersionUID = 5228160894980069094L; 626 627 /** 628 * Get the role of this object. 629 * 630 * @return an instance of AccessibleRole describing the role of the 631 * object 632 */ 633 public AccessibleRole getAccessibleRole() { 634 return AccessibleRole.MENU; 635 } 636 637 } // class AccessibleAWTMenu 638 639} 640