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