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