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