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 }