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