Source for javax.swing.JMenu

   1: /* JMenu.java --
   2:    Copyright (C) 2002, 2004, 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Component;
  42: import java.awt.Point;
  43: import java.awt.PopupMenu;
  44: import java.awt.event.KeyEvent;
  45: import java.awt.event.WindowAdapter;
  46: import java.awt.event.WindowEvent;
  47: import java.beans.PropertyChangeEvent;
  48: import java.beans.PropertyChangeListener;
  49: import java.io.Serializable;
  50: import java.util.ArrayList;
  51: import java.util.EventListener;
  52: 
  53: import javax.accessibility.Accessible;
  54: import javax.accessibility.AccessibleContext;
  55: import javax.accessibility.AccessibleRole;
  56: import javax.accessibility.AccessibleSelection;
  57: import javax.swing.event.MenuEvent;
  58: import javax.swing.event.MenuListener;
  59: import javax.swing.plaf.MenuItemUI;
  60: 
  61: /**
  62:  * This class represents a menu that can be added to a menu bar or
  63:  * can be a submenu in some other menu. When JMenu is selected it
  64:  * displays JPopupMenu containing its menu items.
  65:  *
  66:  * <p>
  67:  * JMenu's fires MenuEvents when this menu's selection changes. If this menu
  68:  * is selected, then fireMenuSelectedEvent() is invoked. In case when menu is
  69:  * deselected or cancelled, then fireMenuDeselectedEvent() or 
  70:  * fireMenuCancelledEvent() is invoked, respectivelly.
  71:  * </p>
  72:  */
  73: public class JMenu extends JMenuItem implements Accessible, MenuElement
  74: {
  75:   private static final long serialVersionUID = 4227225638931828014L;
  76: 
  77:   /** A Popup menu associated with this menu, which pops up when menu is selected */
  78:   private JPopupMenu popupMenu = null;
  79: 
  80:   /** Whenever menu is selected or deselected the MenuEvent is fired to
  81:      menu's registered listeners. */
  82:   private MenuEvent menuEvent = new MenuEvent(this);
  83: 
  84:   /*Amount of time, in milliseconds, that should pass before popupMenu
  85:     associated with this menu appears or disappers */
  86:   private int delay;
  87: 
  88:   /* PopupListener */
  89:   protected WinListener popupListener;
  90: 
  91:   /** Location at which popup menu associated with this menu will be
  92:      displayed */
  93:   private Point menuLocation;
  94: 
  95:   /**
  96:    * Creates a new JMenu object.
  97:    */
  98:   public JMenu()
  99:   {
 100:     super();
 101:     setOpaque(false);
 102:     setDelay(200);
 103:   }
 104: 
 105:   /**
 106:    * Creates a new <code>JMenu</code> with the specified label.
 107:    *
 108:    * @param text label for this menu
 109:    */
 110:   public JMenu(String text)
 111:   {
 112:     super(text);
 113:     popupMenu = new JPopupMenu(); 
 114:     popupMenu.setInvoker(this);
 115:     setOpaque(false);
 116:     setDelay(200);
 117:   }
 118: 
 119:   /**
 120:    * Creates a new <code>JMenu</code> object.
 121:    *
 122:    * @param action Action that is used to create menu item tha will be
 123:    * added to the menu.
 124:    */
 125:   public JMenu(Action action)
 126:   {
 127:     super(action);
 128:     createActionChangeListener(this);
 129:     popupMenu = new JPopupMenu();
 130:     popupMenu.setInvoker(this);
 131:     setOpaque(false);
 132:     setDelay(200);
 133:   }
 134: 
 135:   /**
 136:    * Creates a new <code>JMenu</code> with specified label and an option
 137:    * for this menu to be tear-off menu.
 138:    *
 139:    * @param text label for this menu
 140:    * @param tearoff true if this menu should be tear-off and false otherwise
 141:    */
 142:   public JMenu(String text, boolean tearoff)
 143:   {
 144:     // FIXME: tearoff not implemented
 145:     this(text);
 146:     setDelay(200);
 147:   }
 148: 
 149:   /**
 150:    * Adds specified menu item to this menu
 151:    *
 152:    * @param item Menu item to add to this menu
 153:    *
 154:    * @return Menu item that was added
 155:    */
 156:   public JMenuItem add(JMenuItem item)
 157:   {
 158:     return getPopupMenu().add(item);
 159:   }
 160: 
 161:   /**
 162:    * Adds specified component to this menu.
 163:    *
 164:    * @param component Component to add to this menu
 165:    *
 166:    * @return Component that was added
 167:    */
 168:   public Component add(Component component)
 169:   {
 170:     getPopupMenu().insert(component, -1);
 171:     return component;
 172:   }
 173: 
 174:   /**
 175:    * Adds specified component to this menu at the given index
 176:    *
 177:    * @param component Component to add
 178:    * @param index Position of this menu item in the menu
 179:    *
 180:    * @return Component that was added
 181:    */
 182:   public Component add(Component component, int index)
 183:   {
 184:     return getPopupMenu().add(component, index);
 185:   }
 186: 
 187:   /**
 188:    * Adds JMenuItem constructed with the specified label to this menu
 189:    *
 190:    * @param text label for the menu item that will be added
 191:    *
 192:    * @return Menu Item that was added to this menu
 193:    */
 194:   public JMenuItem add(String text)
 195:   {
 196:     return getPopupMenu().add(text);
 197:   }
 198: 
 199:   /**
 200:    * Adds JMenuItem constructed using properties from specified action.
 201:    *
 202:    * @param action action to construct the menu item with
 203:    *
 204:    * @return Menu Item that was added to this menu
 205:    */
 206:   public JMenuItem add(Action action)
 207:   {
 208:     return getPopupMenu().add(action);
 209:   }
 210: 
 211:   /**
 212:    * Removes given menu item from this menu. Nothing happens if
 213:    * this menu doesn't contain specified menu item.
 214:    *
 215:    * @param item Menu Item which needs to be removed
 216:    */
 217:   public void remove(JMenuItem item)
 218:   {
 219:     getPopupMenu().remove(item);
 220:   }
 221: 
 222:   /**
 223:    * Removes component at the specified index from this menu
 224:    *
 225:    * @param index Position of the component that needs to be removed in the menu
 226:    */
 227:   public void remove(int index)
 228:   {
 229:     if (index < 0 || (index > 0 && getMenuComponentCount() == 0))
 230:       throw new IllegalArgumentException();
 231:   
 232:     if (getMenuComponentCount() > 0)
 233:       popupMenu.remove(index);
 234:   }
 235: 
 236:   /**
 237:    * Removes given component from this menu.
 238:    *
 239:    * @param component Component to remove
 240:    */
 241:   public void remove(Component component)
 242:   {
 243:     int index = getPopupMenu().getComponentIndex(component);
 244:     if (index >= 0)
 245:       getPopupMenu().remove(index);
 246:   }
 247: 
 248:   /**
 249:    * Removes all menu items from the menu
 250:    */
 251:   public void removeAll()
 252:   {
 253:     if (popupMenu != null)
 254:       popupMenu.removeAll();
 255:   }
 256: 
 257:   /**
 258:    * Creates JMenuItem with the specified text and inserts it in the
 259:    * at the specified index
 260:    *
 261:    * @param text label for the new menu item
 262:    * @param index index at which to insert newly created menu item.
 263:    */
 264:   public void insert(String text, int index)
 265:   {
 266:     this.insert(new JMenuItem(text), index);
 267:   }
 268: 
 269:   /**
 270:    * Creates JMenuItem with the specified text and inserts it in the
 271:    * at the specified index. IllegalArgumentException is thrown
 272:    * if index is less than 0
 273:    *
 274:    * @param item menu item to insert
 275:    * @param index index at which to insert menu item.
 276:    * @return Menu item that was added to the menu
 277:    */
 278:   public JMenuItem insert(JMenuItem item, int index)
 279:   {
 280:     if (index < 0)
 281:       throw new IllegalArgumentException("index less than zero");
 282: 
 283:     getPopupMenu().insert(item, index);
 284:     return item;
 285:   }
 286: 
 287:   /**
 288:    * Creates JMenuItem with the associated action and inserts it to the menu
 289:    * at the specified index. IllegalArgumentException is thrown
 290:    * if index is less than 0
 291:    *
 292:    * @param action Action for the new menu item
 293:    * @param index index at which to insert newly created menu item.
 294:    * @return Menu item that was added to the menu
 295:    */
 296:   public JMenuItem insert(Action action, int index)
 297:   {
 298:     JMenuItem item = new JMenuItem(action);
 299:     this.insert(item, index);
 300: 
 301:     return item;
 302:   }
 303: 
 304:   /**
 305:    * This method sets this menuItem's UI to the UIManager's default for the
 306:    * current look and feel.
 307:    */
 308:   public void updateUI()
 309:   {
 310:     setUI((MenuItemUI) UIManager.getUI(this));
 311:   }
 312: 
 313:   /**
 314:    * This method returns a name to identify which look and feel class will be
 315:    * the UI delegate for the menu.
 316:    *
 317:    * @return The Look and Feel classID. "MenuUI"
 318:    */
 319:   public String getUIClassID()
 320:   {
 321:     return "MenuUI";
 322:   }
 323: 
 324:   /**
 325:    * Sets model for this menu.
 326:    *
 327:    * @param model model to set
 328:    */
 329:   public void setModel(ButtonModel model)
 330:   {
 331:     super.setModel(model);
 332:   }
 333: 
 334:   /**
 335:    * Returns true if the menu is selected and false otherwise
 336:    *
 337:    * @return true if the menu is selected and false otherwise
 338:    */
 339:   public boolean isSelected()
 340:   {
 341:     return super.isSelected();
 342:   }
 343: 
 344:   /**
 345:    * A helper method to handle setSelected calls from both mouse events and 
 346:    * direct calls to setSelected.  Direct calls shouldn't expand the popup
 347:    * menu and should select the JMenu even if it is disabled.  Mouse events
 348:    * only select the JMenu if it is enabled and should expand the popup menu
 349:    * associated with this JMenu.
 350:    * @param selected whether or not the JMenu was selected
 351:    * @param menuEnabled whether or not selecting the menu is "enabled".  This
 352:    * is always true for direct calls, and is set to isEnabled() for mouse 
 353:    * based calls.
 354:    * @param showMenu whether or not to show the popup menu
 355:    */
 356:   private void setSelectedHelper(boolean selected, boolean menuEnabled, boolean showMenu)
 357:   {
 358:     // If menu is selected and enabled, activates the menu and 
 359:     // displays associated popup.    
 360:     if (selected && menuEnabled)
 361:       {
 362:     super.setArmed(true);
 363:     super.setSelected(true);
 364: 
 365:         // FIXME: The popup menu should be shown on the screen after certain
 366:         // number of seconds pass. The 'delay' property of this menu indicates
 367:         // this amount of seconds. 'delay' property is 0 by default.
 368:     if (isShowing())
 369:       {
 370:         fireMenuSelected();
 371:             
 372:         int x = 0;
 373:         int y = 0;
 374:             if (showMenu)
 375:               if (menuLocation == null)
 376:                 {
 377:                   // Calculate correct position of the popup. Note that location of the popup 
 378:                   // passed to show() should be relative to the popup's invoker
 379:                   if (isTopLevelMenu())
 380:                     y = this.getHeight();
 381:                   else
 382:                     x = this.getWidth();
 383:                   getPopupMenu().show(this, x, y);
 384:                 }
 385:               else
 386:                 {
 387:                   getPopupMenu().show(this, menuLocation.x, menuLocation.y);
 388:                 }
 389:       }
 390:       }
 391:     
 392:     else
 393:       {
 394:     super.setSelected(false);
 395:     super.setArmed(false);
 396:     fireMenuDeselected();
 397:         getPopupMenu().setVisible(false);
 398:       }
 399:   }
 400: 
 401:   /**
 402:    * Changes this menu selected state if selected is true and false otherwise
 403:    * This method fires menuEvents to menu's registered listeners.
 404:    *
 405:    * @param selected true if the menu should be selected and false otherwise
 406:    */
 407:   public void setSelected(boolean selected)
 408:   {
 409:     setSelectedHelper(selected, true, false); 
 410:   }
 411: 
 412:   /**
 413:    * Checks if PopupMenu associated with this menu is visible
 414:    *
 415:    * @return true if the popup associated with this menu is currently visible
 416:    * on the screen and false otherwise.
 417:    */
 418:   public boolean isPopupMenuVisible()
 419:   {
 420:     return getPopupMenu().isVisible();
 421:   }
 422: 
 423:   /**
 424:    * Sets popup menu visibility
 425:    *
 426:    * @param popup true if popup should be visible and false otherwise
 427:    */
 428:   public void setPopupMenuVisible(boolean popup)
 429:   {
 430:     if (getModel().isEnabled())
 431:       getPopupMenu().setVisible(popup);
 432:   }
 433: 
 434:   /**
 435:    * Returns origin point of the popup menu
 436:    *
 437:    * @return Point containing
 438:    */
 439:   protected Point getPopupMenuOrigin()
 440:   {
 441:     // if menu in the menu bar
 442:     if (isTopLevelMenu())
 443:       return new Point(0, this.getHeight());
 444: 
 445:     // if submenu            
 446:     return new Point(this.getWidth(), 0);
 447:   }
 448: 
 449:   /**
 450:    * Returns delay property.
 451:    *
 452:    * @return delay property, indicating number of milliseconds before
 453:    * popup menu associated with the menu appears or disappears after
 454:    * menu was selected or deselected respectively
 455:    */
 456:   public int getDelay()
 457:   {
 458:     return delay;
 459:   }
 460: 
 461:   /**
 462:    * Sets delay property for this menu. If given time for the delay
 463:    * property is negative, then IllegalArgumentException is thrown
 464:    *
 465:    * @param delay number of milliseconds before
 466:    * popup menu associated with the menu appears or disappears after
 467:    * menu was selected or deselected respectively
 468:    */
 469:   public void setDelay(int delay)
 470:   {
 471:     if (delay < 0)
 472:       throw new IllegalArgumentException("delay less than 0");
 473:     this.delay = delay;
 474:   }
 475: 
 476:   /**
 477:    * Sets location at which popup menu should be displayed
 478:    * The location given is relative to this menu item
 479:    *
 480:    * @param x x-coordinate of the menu location
 481:    * @param y y-coordinate of the menu location
 482:    */
 483:   public void setMenuLocation(int x, int y)
 484:   {
 485:     menuLocation = new Point(x, y);
 486:   }
 487: 
 488:   /**
 489:    * Creates and returns JMenuItem associated with the given action
 490:    *
 491:    * @param action Action to use for creation of JMenuItem
 492:    *
 493:    * @return JMenuItem that was creted with given action
 494:    */
 495:   protected JMenuItem createActionComponent(Action action)
 496:   {
 497:     return new JMenuItem(action);
 498:   }
 499: 
 500:   /**
 501:    * Creates ActionChangeListener to listen for PropertyChangeEvents occuring
 502:    * in the action that is associated with this menu
 503:    *
 504:    * @param item menu that contains action to listen to
 505:    *
 506:    * @return The PropertyChangeListener
 507:    */
 508:   protected PropertyChangeListener createActionChangeListener(JMenuItem item)
 509:   {
 510:     return new ActionChangedListener(item);
 511:   }
 512: 
 513:   /**
 514:    * Adds separator to the end of the menu items in the menu.
 515:    */
 516:   public void addSeparator()
 517:   {
 518:     getPopupMenu().addSeparator();
 519:   }
 520: 
 521:   /**
 522:    * Inserts separator in the menu at the specified index.
 523:    *
 524:    * @param index Index at which separator should be inserted
 525:    */
 526:   public void insertSeparator(int index)
 527:   {
 528:     if (index < 0)
 529:       throw new IllegalArgumentException("index less than 0");
 530: 
 531:     getPopupMenu().insert(new JPopupMenu.Separator(), index);
 532:   }
 533: 
 534:   /**
 535:    * Returns menu item located at the specified index in the menu
 536:    *
 537:    * @param index Index at which to look for the menu item
 538:    *
 539:    * @return menu item located at the specified index in the menu
 540:    */
 541:   public JMenuItem getItem(int index)
 542:   {
 543:     if (index < 0)
 544:       throw new IllegalArgumentException("index less than 0");
 545: 
 546:     if (getItemCount() == 0)
 547:       return null;
 548:     
 549:     Component c = popupMenu.getComponentAtIndex(index);
 550: 
 551:     if (c instanceof JMenuItem)
 552:       return (JMenuItem) c;
 553:     else
 554:       return null;
 555:   }
 556: 
 557:   /**
 558:    * Returns number of items in the menu including separators.
 559:    *
 560:    * @return number of items in the menu
 561:    *
 562:    * @see #getMenuComponentCount()
 563:    */
 564:   public int getItemCount()
 565:   {
 566:     return getMenuComponentCount();
 567:   }
 568: 
 569:   /**
 570:    * Checks if this menu is a tear-off menu.
 571:    *
 572:    * @return true if this menu is a tear-off menu and false otherwise
 573:    */
 574:   public boolean isTearOff()
 575:   {
 576:     // NOT YET IMPLEMENTED 
 577:     throw new Error("The method isTearOff() has not yet been implemented.");
 578:   }
 579: 
 580:   /**
 581:    * Returns number of menu components in this menu
 582:    *
 583:    * @return number of menu components in this menu
 584:    */
 585:   public int getMenuComponentCount()
 586:   {
 587:     return getPopupMenu().getComponentCount();
 588:   }
 589: 
 590:   /**
 591:    * Returns menu component located at the givent index
 592:    * in the menu
 593:    *
 594:    * @param index index at which to get the menu component in the menu
 595:    *
 596:    * @return Menu Component located in the menu at the specified index
 597:    */
 598:   public Component getMenuComponent(int index)
 599:   {
 600:     if (getPopupMenu() == null || getMenuComponentCount() == 0)
 601:       return null;
 602:     
 603:     return (Component) popupMenu.getComponentAtIndex(index);
 604:   }
 605: 
 606:   /**
 607:    * Return components belonging to this menu
 608:    *
 609:    * @return components belonging to this menu
 610:    */
 611:   public Component[] getMenuComponents()
 612:   {
 613:     return getPopupMenu().getComponents();
 614:   }
 615: 
 616:   /**
 617:    * Checks if this menu is a top level menu. The menu is top
 618:    * level menu if it is inside the menu bar. While if the menu
 619:    * inside some other menu, it is considered to be a pull-right menu.
 620:    *
 621:    * @return true if this menu is top level menu, and false otherwise
 622:    */
 623:   public boolean isTopLevelMenu()
 624:   {
 625:     return getParent() instanceof JMenuBar;
 626:   }
 627: 
 628:   /**
 629:    * Checks if given component exists in this menu. The submenus of
 630:    * this menu are checked as well
 631:    *
 632:    * @param component Component to look for
 633:    *
 634:    * @return true if the given component exists in this menu, and false otherwise
 635:    */
 636:   public boolean isMenuComponent(Component component)
 637:   {
 638:     return false;
 639:   }
 640: 
 641:   /**
 642:    * Returns popup menu associated with the menu.
 643:    *
 644:    * @return popup menu associated with the menu.
 645:    */
 646:   public JPopupMenu getPopupMenu()
 647:   {
 648:     if (popupMenu == null)
 649:       {
 650:         popupMenu = new JPopupMenu();
 651:         popupMenu.setInvoker(this);
 652:       }
 653:     return popupMenu;
 654:   }
 655: 
 656:   /**
 657:    * Adds MenuListener to the menu
 658:    *
 659:    * @param listener MenuListener to add
 660:    */
 661:   public void addMenuListener(MenuListener listener)
 662:   {
 663:     listenerList.add(MenuListener.class, listener);
 664:   }
 665: 
 666:   /**
 667:    * Removes MenuListener from the menu
 668:    *
 669:    * @param listener MenuListener to remove
 670:    */
 671:   public void removeMenuListener(MenuListener listener)
 672:   {
 673:     listenerList.remove(MenuListener.class, listener);
 674:   }
 675: 
 676:   /**
 677:    * Returns all registered <code>MenuListener</code> objects.
 678:    *
 679:    * @return an array of listeners
 680:    * 
 681:    * @since 1.4
 682:    */
 683:   public MenuListener[] getMenuListeners()
 684:   {
 685:     return (MenuListener[]) listenerList.getListeners(MenuListener.class);
 686:   }
 687: 
 688:   /**
 689:    * This method fires MenuEvents to all menu's MenuListeners. In this case
 690:    * menuSelected() method of MenuListeners is called to indicated that the menu
 691:    * was selected.
 692:    */
 693:   protected void fireMenuSelected()
 694:   {
 695:     MenuListener[] listeners = getMenuListeners();
 696: 
 697:     for (int index = 0; index < listeners.length; ++index)
 698:       listeners[index].menuSelected(menuEvent);
 699:   }
 700: 
 701:   /**
 702:    * This method fires MenuEvents to all menu's MenuListeners. In this case
 703:    * menuDeselected() method of MenuListeners is called to indicated that the menu
 704:    * was deselected.
 705:    */
 706:   protected void fireMenuDeselected()
 707:   {
 708:     EventListener[] ll = listenerList.getListeners(MenuListener.class);
 709: 
 710:     for (int i = 0; i < ll.length; i++)
 711:       ((MenuListener) ll[i]).menuDeselected(menuEvent);
 712:   }
 713: 
 714:   /**
 715:    * This method fires MenuEvents to all menu's MenuListeners. In this case
 716:    * menuSelected() method of MenuListeners is called to indicated that the menu
 717:    * was cancelled. The menu is cancelled when it's popup menu is close without selection.
 718:    */
 719:   protected void fireMenuCanceled()
 720:   {
 721:     EventListener[] ll = listenerList.getListeners(MenuListener.class);
 722: 
 723:     for (int i = 0; i < ll.length; i++)
 724:       ((MenuListener) ll[i]).menuCanceled(menuEvent);
 725:   }
 726: 
 727:   /**
 728:    * Creates WinListener that listens to the menu;s popup menu.
 729:    *
 730:    * @param popup JPopupMenu to listen to
 731:    *
 732:    * @return The WinListener
 733:    */
 734:   protected WinListener createWinListener(JPopupMenu popup)
 735:   {
 736:     return new WinListener(popup);
 737:   }
 738: 
 739:   /**
 740:    * Method of the MenuElementInterface. It reacts to the selection
 741:    * changes in the menu. If this menu was selected, then it
 742:    * displayes popup menu associated with it and if this menu was
 743:    * deselected it hides the popup menu.
 744:    *
 745:    * @param changed true if the menu was selected and false otherwise
 746:    */
 747:   public void menuSelectionChanged(boolean changed)
 748:   {
 749:     // if this menu selection is true, then activate this menu and 
 750:     // display popup associated with this menu
 751:     setSelectedHelper(changed, isEnabled(), true);
 752:   }
 753: 
 754:   /**
 755:    * Method of MenuElement interface. Returns sub components of
 756:    * this menu.
 757:    *
 758:    * @return array containing popupMenu that is associated with this menu
 759:    */
 760:   public MenuElement[] getSubElements()
 761:   {
 762:     return new MenuElement[] { popupMenu };
 763:   }
 764: 
 765:   /**
 766:    * @return Returns reference to itself
 767:    */
 768:   public Component getComponent()
 769:   {
 770:     return this;
 771:   }
 772: 
 773:   /**
 774:    * This method is overriden with empty implementation, s.t the
 775:    * accelerator couldn't be set for the menu. The mnemonic should
 776:    * be used for the menu instead.
 777:    *
 778:    * @param keystroke accelerator for this menu
 779:    */
 780:   public void setAccelerator(KeyStroke keystroke)
 781:   {
 782:     throw new Error("setAccelerator() is not defined for JMenu.  Use setMnemonic() instead.");
 783:   }
 784: 
 785:   /**
 786:    * This method process KeyEvent occuring when the menu is visible
 787:    *
 788:    * @param event The KeyEvent
 789:    */
 790:   protected void processKeyEvent(KeyEvent event)
 791:   {
 792:     MenuSelectionManager.defaultManager().processKeyEvent(event);
 793:   }
 794: 
 795:   /**
 796:    * Programatically performs click
 797:    *
 798:    * @param time Number of milliseconds for which this menu stays pressed
 799:    */
 800:   public void doClick(int time)
 801:   {
 802:     getModel().setArmed(true);
 803:     getModel().setPressed(true);
 804:     try
 805:       {
 806:     java.lang.Thread.sleep(time);
 807:       }
 808:     catch (java.lang.InterruptedException e)
 809:       {
 810:     // probably harmless
 811:       }
 812: 
 813:     getModel().setPressed(false);
 814:     getModel().setArmed(false);
 815:     popupMenu.show(this, this.getWidth(), 0);
 816:   }
 817: 
 818:   /**
 819:    * A string that describes this JMenu. Normally only used
 820:    * for debugging.
 821:    *
 822:    * @return A string describing this JMenu
 823:    */
 824:   protected String paramString()
 825:   {
 826:     return super.paramString();
 827:   }
 828: 
 829:   public AccessibleContext getAccessibleContext()
 830:   {
 831:     if (accessibleContext == null)
 832:       accessibleContext = new AccessibleJMenu();
 833: 
 834:     return accessibleContext;
 835:   }
 836: 
 837:   /**
 838:    * Implements support for assisitive technologies for <code>JMenu</code>.
 839:    */
 840:   protected class AccessibleJMenu extends AccessibleJMenuItem
 841:     implements AccessibleSelection
 842:   {
 843:     private static final long serialVersionUID = -8131864021059524309L;
 844: 
 845:     protected AccessibleJMenu()
 846:     {
 847:       // Nothing to do here.
 848:     }
 849: 
 850:     /**
 851:      * Returns the number of accessible children of this object.
 852:      *
 853:      * @return the number of accessible children of this object
 854:      */
 855:     public int getAccessibleChildrenCount()
 856:     {
 857:       Component[] children = getMenuComponents();
 858:       int count = 0;
 859:       for (int i = 0; i < children.length; i++)
 860:         {
 861:           if (children[i] instanceof Accessible)
 862:             count++;
 863:         }
 864:       return count;
 865:     }
 866: 
 867:     /**
 868:      * Returns the accessible child with the specified <code>index</code>.
 869:      *
 870:      * @param index the index of the child to fetch
 871:      *
 872:      * @return the accessible child with the specified <code>index</code>
 873:      */
 874:     public Accessible getAccessibleChild(int index)
 875:     {
 876:       Component[] children = getMenuComponents();
 877:       int count = 0;
 878:       Accessible found = null;
 879:       for (int i = 0; i < children.length; i++)
 880:         {
 881:           if (children[i] instanceof Accessible)
 882:             {
 883:               if (count == index)
 884:                 {
 885:                   found = (Accessible) children[i];
 886:                   break;
 887:                 }
 888:               count++;
 889:             }
 890:         }
 891:       return found;
 892:     }
 893: 
 894:     /**
 895:      * Returns the accessible selection of this object. AccessibleJMenus handle
 896:      * their selection themselves, so we always return <code>this</code> here.
 897:      *
 898:      * @return the accessible selection of this object
 899:      */
 900:     public AccessibleSelection getAccessibleSelection()
 901:     {
 902:       return this;
 903:     }
 904: 
 905:     /**
 906:      * Returns the selected accessible child with the specified
 907:      * <code>index</code>.
 908:      *
 909:      * @param index the index of the accessible selected child to return
 910:      *
 911:      * @return the selected accessible child with the specified
 912:      *         <code>index</code>
 913:      */
 914:     public Accessible getAccessibleSelection(int index)
 915:     {
 916:       Accessible selected = null;
 917:       // Only one item can be selected, which must therefore have index == 0.
 918:       if (index == 0)
 919:         {
 920:           MenuSelectionManager msm = MenuSelectionManager.defaultManager();
 921:           MenuElement[] me = msm.getSelectedPath();
 922:           if (me != null)
 923:             {
 924:               for (int i = 0; i < me.length; i++)
 925:                 {
 926:                   if (me[i] == JMenu.this)
 927:                     {
 928:                       // This JMenu is selected, find and return the next
 929:                       // JMenuItem in the path.
 930:                       do
 931:                         {
 932:                           if (me[i] instanceof Accessible)
 933:                             {
 934:                               selected = (Accessible) me[i];
 935:                               break;
 936:                             }
 937:                           i++;
 938:                         } while (i < me.length);
 939:                     }
 940:                   if (selected != null)
 941:                     break;
 942:                 }
 943:             }
 944:         }
 945:       return selected;
 946:     }
 947: 
 948:     /**
 949:      * Returns <code>true</code> if the accessible child with the specified
 950:      * index is selected, <code>false</code> otherwise.
 951:      *
 952:      * @param index the index of the accessible child to check
 953:      *
 954:      * @return <code>true</code> if the accessible child with the specified
 955:      *         index is selected, <code>false</code> otherwise
 956:      */
 957:     public boolean isAccessibleChildSelected(int index)
 958:     {
 959:       boolean selected = false;
 960:       MenuSelectionManager msm = MenuSelectionManager.defaultManager();
 961:       MenuElement[] me = msm.getSelectedPath();
 962:       if (me != null)
 963:         {
 964:           Accessible toBeFound = getAccessibleChild(index);
 965:           for (int i = 0; i < me.length; i++)
 966:             {
 967:               if (me[i] == toBeFound)
 968:                 {
 969:                   selected = true;
 970:                   break;
 971:                 }
 972:             }
 973:         }
 974:       return selected;
 975:     }
 976: 
 977:     /**
 978:      * Returns the accessible role of this object, which is
 979:      * {@link AccessibleRole#MENU} for the AccessibleJMenu.
 980:      *
 981:      * @return the accessible role of this object
 982:      */
 983:     public AccessibleRole getAccessibleRole()
 984:     {
 985:       return AccessibleRole.MENU;
 986:     }
 987: 
 988:     /**
 989:      * Returns the number of selected accessible children. This will be
 990:      * <code>0</code> if no item is selected, or <code>1</code> if an item
 991:      * is selected. AccessibleJMenu can have maximum 1 selected item.
 992:      *
 993:      * @return the number of selected accessible children
 994:      */
 995:     public int getAccessibleSelectionCount()
 996:     {
 997:       int count = 0;
 998:       MenuSelectionManager msm = MenuSelectionManager.defaultManager();
 999:       MenuElement[] me = msm.getSelectedPath();
1000:       if (me != null)
1001:         {
1002:           for (int i = 0; i < me.length; i++)
1003:             {
1004:               if (me[i] == JMenu.this)
1005:                 {
1006:                   if (i + 1 < me.length)
1007:                     {
1008:                       count = 1;
1009:                       break;
1010:                     }
1011:                 }
1012:             }
1013:         }
1014:       return count;
1015:     }
1016: 
1017:     /**
1018:      * Selects the accessible child with the specified index.
1019:      *
1020:      * @param index the index of the accessible child to select
1021:      */
1022:     public void addAccessibleSelection(int index)
1023:     {
1024:       Accessible child = getAccessibleChild(index);
1025:       if (child != null && child instanceof JMenuItem)
1026:         {
1027:           JMenuItem mi = (JMenuItem) child;
1028:           MenuSelectionManager msm = MenuSelectionManager.defaultManager();
1029:           msm.setSelectedPath(createPath(JMenu.this));
1030:         }
1031:     }
1032: 
1033:     /**
1034:      * Removes the item with the specified index from the selection.
1035:      *
1036:      * @param index the index of the selected item to remove from the selection
1037:      */
1038:     public void removeAccessibleSelection(int index)
1039:     {
1040:       Accessible child = getAccessibleChild(index);
1041:       if (child != null)
1042:         {
1043:           MenuSelectionManager msm = MenuSelectionManager.defaultManager();
1044:           MenuElement[] oldSelection = msm.getSelectedPath();
1045:           for (int i = 0; i < oldSelection.length; i++)
1046:             {
1047:               if (oldSelection[i] == child)
1048:                 {
1049:                   // Found the specified child in the selection. Remove it
1050:                   // from the selection.
1051:                   MenuElement[] newSel = new MenuElement[i - 1];
1052:                   System.arraycopy(oldSelection, 0, newSel, 0, i - 1);
1053:                   msm.setSelectedPath(newSel);
1054:                   break;
1055:                 }
1056:             }
1057:         }
1058:     }
1059: 
1060:     /**
1061:      * Removes all possibly selected accessible children of this object from
1062:      * the selection.
1063:      */
1064:     public void clearAccessibleSelection()
1065:     {
1066:       MenuSelectionManager msm = MenuSelectionManager.defaultManager();
1067:       MenuElement[] oldSelection = msm.getSelectedPath();
1068:       for (int i = 0; i < oldSelection.length; i++)
1069:         {
1070:           if (oldSelection[i] == JMenu.this)
1071:             {
1072:               // Found this menu in the selection. Remove all children from
1073:               // the selection.
1074:               MenuElement[] newSel = new MenuElement[i];
1075:               System.arraycopy(oldSelection, 0, newSel, 0, i);
1076:               msm.setSelectedPath(newSel);
1077:               break;
1078:             }
1079:         }
1080:     }
1081: 
1082:     /**
1083:      * AccessibleJMenu don't support multiple selection, so this method
1084:      * does nothing.
1085:      */
1086:     public void selectAllAccessibleSelection()
1087:     {
1088:       // Nothing to do here.
1089:     }
1090:   }
1091: 
1092:   protected class WinListener extends WindowAdapter implements Serializable
1093:   {
1094:     private static final long serialVersionUID = -6415815570638474823L;
1095: 
1096:     /**
1097:      * Creates a new <code>WinListener</code>.
1098:      *
1099:      * @param popup the popup menu which is observed
1100:      */
1101:     public WinListener(JPopupMenu popup)
1102:     {
1103:       // TODO: What should we do with the popup argument?
1104:     }
1105: 
1106:     /**
1107:      * Receives notification when the popup menu is closing and deselects
1108:      * the menu.
1109:      *
1110:      * @param event the window event
1111:      */
1112:     public void windowClosing(WindowEvent event)
1113:     {
1114:       setSelected(false);
1115:     }
1116:   }
1117: 
1118:   /**
1119:    * This class listens to PropertyChangeEvents occuring in menu's action
1120:    */
1121:   private class ActionChangedListener implements PropertyChangeListener
1122:   {
1123:     /** menu item associated with the action */
1124:     private JMenuItem menuItem;
1125: 
1126:     /** Creates new ActionChangedListener and adds it to menuItem's action */
1127:     public ActionChangedListener(JMenuItem menuItem)
1128:     {
1129:       this.menuItem = menuItem;
1130: 
1131:       Action a = menuItem.getAction();
1132:       if (a != null)
1133:     a.addPropertyChangeListener(this);
1134:     }
1135: 
1136:     /**This method is invoked when some change occures in menuItem's action*/
1137:     public void propertyChange(PropertyChangeEvent evt)
1138:     {
1139:       // FIXME: Need to implement
1140:     }
1141:   }
1142: 
1143:   /**
1144:    * Creates an array to be feeded as argument to
1145:    * {@link MenuSelectionManager#setSelectedPath(MenuElement[])} for the
1146:    * specified element. This has the effect of selecting the specified element
1147:    * and all its parents.
1148:    *
1149:    * @param leaf the leaf element for which to create the selected path
1150:    *
1151:    * @return the selected path array
1152:    */
1153:   MenuElement[] createPath(JMenu leaf)
1154:   {
1155:     ArrayList path = new ArrayList();
1156:     MenuElement[] array = null;
1157:     Component current = leaf.getPopupMenu();
1158:     while (true)
1159:       {
1160:         if (current instanceof JPopupMenu)
1161:           {
1162:             JPopupMenu popupMenu = (JPopupMenu) current;
1163:             path.add(0, popupMenu);
1164:             current = popupMenu.getInvoker();
1165:           }
1166:         else if (current instanceof JMenu)
1167:           {
1168:             JMenu menu = (JMenu) current;
1169:             path.add(0, menu);
1170:             current = menu.getParent();
1171:           }
1172:         else if (current instanceof JMenuBar)
1173:           {
1174:             JMenuBar menuBar = (JMenuBar) current;
1175:             path.add(0, menuBar);
1176:             array = new MenuElement[path.size()];
1177:             array = (MenuElement[]) path.toArray(array);
1178:             break;
1179:           }
1180:       }
1181:     return array;
1182:   }
1183: }