Source for javax.swing.JMenuItem

   1: /* JMenuItem.java --
   2:    Copyright (C) 2002, 2004, 2005  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.event.InputEvent;
  43: import java.awt.event.KeyEvent;
  44: import java.awt.event.MouseEvent;
  45: import java.beans.PropertyChangeEvent;
  46: import java.beans.PropertyChangeListener;
  47: import java.util.EventListener;
  48: 
  49: import javax.accessibility.Accessible;
  50: import javax.accessibility.AccessibleContext;
  51: import javax.accessibility.AccessibleRole;
  52: import javax.swing.event.ChangeEvent;
  53: import javax.swing.event.ChangeListener;
  54: import javax.swing.event.MenuDragMouseEvent;
  55: import javax.swing.event.MenuDragMouseListener;
  56: import javax.swing.event.MenuKeyEvent;
  57: import javax.swing.event.MenuKeyListener;
  58: import javax.swing.plaf.MenuItemUI;
  59: 
  60: /**
  61:  * JMenuItem represents element in the menu. It inherits most of
  62:  * its functionality from AbstractButton, however its behavior somewhat
  63:  * varies from it. JMenuItem fire different kinds of events.
  64:  * PropertyChangeEvents are fired when menuItems properties are modified;
  65:  * ChangeEvents are fired when menuItem's state changes and actionEvents are
  66:  * fired when menu item is selected. In addition to this events menuItem also
  67:  * fire MenuDragMouseEvent and MenuKeyEvents when mouse is dragged over
  68:  * the menu item or associated key with menu item is invoked respectively.
  69:  */
  70: public class JMenuItem extends AbstractButton implements Accessible,
  71:                                                          MenuElement
  72: {
  73:   private static final long serialVersionUID = -1681004643499461044L;
  74: 
  75:   /** Combination of keyboard keys that can be used to activate this menu item */
  76:   private KeyStroke accelerator;
  77: 
  78:   /**
  79:    * Creates a new JMenuItem object.
  80:    */
  81:   public JMenuItem()
  82:   {
  83:     super();
  84:     init(null, null);
  85:   }
  86: 
  87:   /**
  88:    * Creates a new JMenuItem with the given icon.
  89:    *
  90:    * @param icon Icon that will be displayed on the menu item
  91:    */
  92:   public JMenuItem(Icon icon)
  93:   {
  94:     // FIXME: The requestedFocusEnabled property should
  95:     // be set to false, when only icon is set for menu item.
  96:     super();
  97:     init(null, icon);
  98:   }
  99: 
 100:   /**
 101:    * Creates a new JMenuItem with the given label.
 102:    *
 103:    * @param text label for the menu item
 104:    */
 105:   public JMenuItem(String text)
 106:   {
 107:     this(text, null);
 108:   }
 109: 
 110:   /**
 111:    * Creates a new JMenuItem associated with the specified action.
 112:    *
 113:    * @param action action for this menu item
 114:    */
 115:   public JMenuItem(Action action)
 116:   {
 117:     super();
 118:     super.setAction(action);
 119:     init(null, null);
 120:   }
 121: 
 122:   /**
 123:    * Creates a new JMenuItem with specified text and icon.
 124:    * Text is displayed to the left of icon by default.
 125:    *
 126:    * @param text label for this menu item
 127:    * @param icon icon that will be displayed on this menu item
 128:    */
 129:   public JMenuItem(String text, Icon icon)
 130:   {
 131:     super();
 132:     init(text, icon);
 133:   }
 134: 
 135:   /**
 136:    * Creates a new JMenuItem object.
 137:    *
 138:    * @param text label for this menu item
 139:    * @param mnemonic - Single key that can be used with a
 140:    * look-and-feel meta key to activate this menu item. However
 141:    * menu item should be visible on the screen when mnemonic is used.
 142:    */
 143:   public JMenuItem(String text, int mnemonic)
 144:   {
 145:     this(text, null);
 146:     setMnemonic(mnemonic);
 147:   }
 148: 
 149:   /**
 150:    * Initializes this menu item
 151:    *
 152:    * @param text label for this menu item
 153:    * @param icon icon to be displayed for this menu item
 154:    */
 155:   protected void init(String text, Icon icon)
 156:   {
 157:     super.init(text, icon);
 158:     setModel(new DefaultButtonModel());
 159: 
 160:     // Initializes properties for this menu item, that are different
 161:     // from Abstract button properties. 
 162:     /* NOTE: According to java specifications paint_border should be set to false,
 163:       since menu item should not have a border. However running few java programs
 164:       it seems that menu items and menues can have a border. Commenting
 165:       out statement below for now. */
 166:     //borderPainted = false;
 167:     focusPainted = false;
 168:     horizontalAlignment = JButton.LEFT;
 169:     horizontalTextPosition = JButton.TRAILING;
 170:   }
 171: 
 172:   /**
 173:    * Set the "UI" property of the menu item, which is a look and feel class
 174:    * responsible for handling menuItem's input events and painting it.
 175:    *
 176:    * @param ui The new "UI" property
 177:    */
 178:   public void setUI(MenuItemUI ui)
 179:   {
 180:     super.setUI(ui);
 181:   }
 182:   
 183:   /**
 184:    * This method sets this menuItem's UI to the UIManager's default for the
 185:    * current look and feel.
 186:    */
 187:   public void updateUI()
 188:   {
 189:     MenuItemUI mi = ((MenuItemUI) UIManager.getUI(this));
 190:     setUI(mi);
 191:     invalidate();
 192:   }
 193: 
 194:   /**
 195:    * This method returns a name to identify which look and feel class will be
 196:    * the UI delegate for the menuItem.
 197:    *
 198:    * @return The Look and Feel classID. "MenuItemUI"
 199:    */
 200:   public String getUIClassID()
 201:   {
 202:     return "MenuItemUI";
 203:   }
 204: 
 205:   /**
 206:    * Returns true if button's model is armed and false otherwise. The
 207:    * button model is armed if menu item has focus or it is selected.
 208:    *
 209:    * @return $boolean$ true if button's model is armed and false otherwise
 210:    */
 211:   public boolean isArmed()
 212:   {
 213:     return getModel().isArmed();
 214:   }
 215: 
 216:   /**
 217:    * Sets menuItem's "ARMED" property
 218:    *
 219:    * @param armed DOCUMENT ME!
 220:    */
 221:   public void setArmed(boolean armed)
 222:   {
 223:     getModel().setArmed(armed);
 224:   }
 225: 
 226:   /**
 227:    * Enable or disable menu item. When menu item is disabled,
 228:    * its text and icon are grayed out if they exist.
 229:    *
 230:    * @param enabled if true enable menu item, and disable otherwise.
 231:    */
 232:   public void setEnabled(boolean enabled)
 233:   {
 234:     super.setEnabled(enabled);
 235:   }
 236: 
 237:   /**
 238:    * Return accelerator for this menu item.
 239:    *
 240:    * @return $KeyStroke$ accelerator for this menu item.
 241:    */
 242:   public KeyStroke getAccelerator()
 243:   {
 244:     return accelerator;
 245:   }
 246: 
 247:   /**
 248:    * Sets the key combination which invokes the menu item's action 
 249:    * listeners without navigating the menu hierarchy. Note that when the 
 250:    * keyboard accelerator is typed, it will work whether or not the 
 251:    * menu is currently displayed.
 252:    * 
 253:    * @param keystroke accelerator for this menu item.
 254:    */
 255:   public void setAccelerator(KeyStroke keystroke)
 256:   {
 257:     this.accelerator = keystroke;
 258:   }
 259: 
 260:   /**
 261:    * Configures menu items' properties from properties of the specified action.
 262:    * This method overrides configurePropertiesFromAction from AbstractButton
 263:    * to also set accelerator property.
 264:    *
 265:    * @param action action to configure properties from
 266:    */
 267:   protected void configurePropertiesFromAction(Action action)
 268:   {
 269:     super.configurePropertiesFromAction(action);
 270: 
 271:     if (! (this instanceof JMenu) && action != null)
 272:       {
 273:         setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY)));
 274:         super.registerKeyboardAction(action, accelerator, 
 275:                                      JComponent.WHEN_IN_FOCUSED_WINDOW);
 276:       }
 277:   }
 278: 
 279:   /**
 280:    * Creates PropertyChangeListener to listen for the changes in action
 281:    * properties.
 282:    *
 283:    * @param action action to listen to for property changes
 284:    *
 285:    * @return $PropertyChangeListener$ Listener that listens to changes in
 286:    * action properties.
 287:    */
 288:   protected PropertyChangeListener createActionPropertyChangeListener(Action action)
 289:   {
 290:     return new PropertyChangeListener()
 291:       {
 292:     public void propertyChange(PropertyChangeEvent e)
 293:     {
 294:       Action act = (Action) (e.getSource());
 295:       configurePropertiesFromAction(act);
 296:     }
 297:       };
 298:   }
 299: 
 300:   /**
 301:    * Process mouse events forwarded from MenuSelectionManager.
 302:    *
 303:    * @param event event forwarded from MenuSelectionManager
 304:    * @param path path to the menu element from which event was generated
 305:    * @param manager MenuSelectionManager for the current menu hierarchy
 306:    */
 307:   public void processMouseEvent(MouseEvent event, MenuElement[] path,
 308:                                 MenuSelectionManager manager)
 309:   {
 310:     // Fire MenuDragMouseEvents if mouse is being dragged.
 311:     boolean dragged
 312:       = (event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0;
 313:     if (dragged)
 314:       processMenuDragMouseEvent(createMenuDragMouseEvent(event, path, manager));
 315: 
 316:     switch (event.getID())
 317:       {
 318:       case MouseEvent.MOUSE_CLICKED:
 319:     break;
 320:       case MouseEvent.MOUSE_ENTERED:
 321:     if (isRolloverEnabled())
 322:       model.setRollover(true);
 323:     break;
 324:       case MouseEvent.MOUSE_EXITED:
 325:     if (isRolloverEnabled())
 326:       model.setRollover(false);
 327: 
 328:     // for JMenu last element on the path is its popupMenu.
 329:     // JMenu shouldn't me disarmed.    
 330:     if (! (path[path.length - 1] instanceof JPopupMenu) && ! dragged)
 331:       setArmed(false);
 332:     break;
 333:       case MouseEvent.MOUSE_PRESSED:
 334:     if ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0)
 335:       {
 336:         model.setArmed(true);
 337:         model.setPressed(true);
 338:       }
 339:     break;
 340:       case MouseEvent.MOUSE_RELEASED:
 341:     break;
 342:       case MouseEvent.MOUSE_MOVED:
 343:     break;
 344:       case MouseEvent.MOUSE_DRAGGED:
 345:     break;
 346:       }
 347:   }
 348: 
 349:   /**
 350:    * Creates MenuDragMouseEvent.
 351:    *
 352:    * @param event MouseEvent that occured while mouse was pressed.
 353:    * @param path Path the the menu element where the dragging event was
 354:    *        originated
 355:    * @param manager MenuSelectionManager for the current menu hierarchy.
 356:    *
 357:    * @return new MenuDragMouseEvent
 358:    */
 359:   private MenuDragMouseEvent createMenuDragMouseEvent(MouseEvent event,
 360:                                                       MenuElement[] path,
 361:                                                       MenuSelectionManager manager)
 362:   {
 363:     return new MenuDragMouseEvent((Component) event.getSource(),
 364:                                   event.getID(), event.getWhen(),
 365:                                   event.getModifiers(), event.getX(),
 366:                                   event.getY(), event.getClickCount(),
 367:                                   event.isPopupTrigger(), path, manager);
 368:   }
 369: 
 370:   /**
 371:    * Process key events forwarded from MenuSelectionManager.
 372:    *
 373:    * @param event event forwarded from MenuSelectionManager
 374:    * @param path path to the menu element from which event was generated
 375:    * @param manager MenuSelectionManager for the current menu hierarchy
 376:    */
 377:   public void processKeyEvent(KeyEvent event, MenuElement[] path,
 378:                               MenuSelectionManager manager)
 379:   {
 380:     // Need to implement.
 381:   }
 382: 
 383:   /**
 384:    * This method fires MenuDragMouseEvents to registered listeners.
 385:    * Different types of MenuDragMouseEvents are fired depending
 386:    * on the observed mouse event.
 387:    *
 388:    * @param event Mouse
 389:    */
 390:   public void processMenuDragMouseEvent(MenuDragMouseEvent event)
 391:   {
 392:     switch (event.getID())
 393:       {
 394:       case MouseEvent.MOUSE_ENTERED:
 395:     fireMenuDragMouseEntered(event);
 396:     break;
 397:       case MouseEvent.MOUSE_EXITED:
 398:     fireMenuDragMouseExited(event);
 399:     break;
 400:       case MouseEvent.MOUSE_DRAGGED:
 401:     fireMenuDragMouseDragged(event);
 402:     break;
 403:       case MouseEvent.MOUSE_RELEASED:
 404:     fireMenuDragMouseReleased(event);
 405:     break;
 406:       }
 407:   }
 408: 
 409:   /**
 410:    * This method fires MenuKeyEvent to registered listeners.
 411:    * Different types of MenuKeyEvents are fired depending
 412:    * on the observed key event.
 413:    *
 414:    * @param event DOCUMENT ME!
 415:    */
 416:   public void processMenuKeyEvent(MenuKeyEvent event)
 417:   {
 418:     // Need to implement.
 419:   }
 420: 
 421:   /**
 422:    * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
 423:    *
 424:    * @param event The event signifying that mouse entered menuItem while it was dragged
 425:    */
 426:   protected void fireMenuDragMouseEntered(MenuDragMouseEvent event)
 427:   {
 428:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 429: 
 430:     for (int i = 0; i < ll.length; i++)
 431:       ((MenuDragMouseListener) ll[i]).menuDragMouseEntered(event);
 432:   }
 433: 
 434:   /**
 435:    * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
 436:    *
 437:    * @param event The event signifying that mouse has exited menu item, while it was dragged
 438:    */
 439:   protected void fireMenuDragMouseExited(MenuDragMouseEvent event)
 440:   {
 441:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 442: 
 443:     for (int i = 0; i < ll.length; i++)
 444:       ((MenuDragMouseListener) ll[i]).menuDragMouseExited(event);
 445:   }
 446: 
 447:   /**
 448:    * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
 449:    *
 450:    * @param event The event signifying that mouse is being dragged over the menuItem
 451:    */
 452:   protected void fireMenuDragMouseDragged(MenuDragMouseEvent event)
 453:   {
 454:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 455: 
 456:     for (int i = 0; i < ll.length; i++)
 457:       ((MenuDragMouseListener) ll[i]).menuDragMouseDragged(event);
 458:   }
 459: 
 460:   /**
 461:    * This method fires a MenuDragMouseEvent to all the MenuItem's MouseInputListeners.
 462:    *
 463:    * @param event The event signifying that mouse was released while it was dragged over the menuItem
 464:    */
 465:   protected void fireMenuDragMouseReleased(MenuDragMouseEvent event)
 466:   {
 467:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 468: 
 469:     for (int i = 0; i < ll.length; i++)
 470:       ((MenuDragMouseListener) ll[i]).menuDragMouseReleased(event);
 471:   }
 472: 
 473:   /**
 474:    * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
 475:    *
 476:    * @param event The event signifying that key associated with this menu was pressed
 477:    */
 478:   protected void fireMenuKeyPressed(MenuKeyEvent event)
 479:   {
 480:     EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
 481: 
 482:     for (int i = 0; i < ll.length; i++)
 483:       ((MenuKeyListener) ll[i]).menuKeyPressed(event);
 484:   }
 485: 
 486:   /**
 487:    * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
 488:    *
 489:    * @param event The event signifying that key associated with this menu was released
 490:    */
 491:   protected void fireMenuKeyReleased(MenuKeyEvent event)
 492:   {
 493:     EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
 494: 
 495:     for (int i = 0; i < ll.length; i++)
 496:       ((MenuKeyListener) ll[i]).menuKeyTyped(event);
 497:   }
 498: 
 499:   /**
 500:    * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
 501:    *
 502:    * @param event The event signifying that key associated with this menu was typed.
 503:    *        The key is typed when it was pressed and then released
 504:    */
 505:   protected void fireMenuKeyTyped(MenuKeyEvent event)
 506:   {
 507:     EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
 508: 
 509:     for (int i = 0; i < ll.length; i++)
 510:       ((MenuKeyListener) ll[i]).menuKeyTyped(event);
 511:   }
 512: 
 513:   /**
 514:    * Method of the MenuElement interface.
 515:    * This method is invoked by MenuSelectionManager when selection of
 516:    * this menu item has changed. If this menu item was selected then
 517:    * arm it's model, and disarm the model otherwise. The menu item
 518:    * is considered to be selected, and thus highlighted when its model
 519:    * is armed.
 520:    *
 521:    * @param changed indicates selection status of this menu item. If changed is
 522:    * true then menu item is selected and deselected otherwise.
 523:    */
 524:   public void menuSelectionChanged(boolean changed)
 525:   {
 526:     Component parent = this.getParent();
 527:     if (changed)
 528:       {
 529:     model.setArmed(true);
 530: 
 531:     if (parent != null && parent instanceof JPopupMenu)
 532:       ((JPopupMenu) parent).setSelected(this);
 533:       }
 534:     else
 535:       {
 536:     model.setArmed(false);
 537: 
 538:     if (parent != null && parent instanceof JPopupMenu)
 539:       ((JPopupMenu) parent).getSelectionModel().clearSelection();
 540:       }
 541:   }
 542: 
 543:   /**
 544:    * Method of the MenuElement interface.
 545:    *
 546:    * @return $MenuElement[]$ Returns array of sub-components for this menu
 547:    *         item. By default menuItem doesn't have any subcomponents and so
 548:    *         empty array is returned instead.
 549:    */
 550:   public MenuElement[] getSubElements()
 551:   {
 552:     return new MenuElement[0];
 553:   }
 554: 
 555:   /**
 556:    * Returns reference to the component that will paint this menu item.
 557:    *
 558:    * @return $Component$ Component that will paint this menu item.
 559:    *         Simply returns reference to this menu item.
 560:    */
 561:   public Component getComponent()
 562:   {
 563:     return this;
 564:   }
 565: 
 566:   /**
 567:    * Adds a MenuDragMouseListener to this menu item. When mouse
 568:    * is dragged over the menu item the MenuDragMouseEvents will be
 569:    * fired, and these listeners will be called.
 570:    *
 571:    * @param listener The new listener to add
 572:    */
 573:   public void addMenuDragMouseListener(MenuDragMouseListener listener)
 574:   {
 575:     listenerList.add(MenuDragMouseListener.class, listener);
 576:   }
 577: 
 578:   /**
 579:    * Removes a MenuDragMouseListener from the menuItem's listener list.
 580:    *
 581:    * @param listener The listener to remove
 582:    */
 583:   public void removeMenuDragMouseListener(MenuDragMouseListener listener)
 584:   {
 585:     listenerList.remove(MenuDragMouseListener.class, listener);
 586:   }
 587: 
 588:   /**
 589:    * Returns all added MenuDragMouseListener objects.
 590:    *
 591:    * @return an array of listeners
 592:    *
 593:    * @since 1.4
 594:    */
 595:   public MenuDragMouseListener[] getMenuDragMouseListeners()
 596:   {
 597:     return (MenuDragMouseListener[]) listenerList.getListeners(MenuDragMouseListener.class);
 598:   }
 599: 
 600:   /**
 601:    * Adds an MenuKeyListener to this menu item.  This listener will be
 602:    * invoked when MenuKeyEvents will be fired by this menu item.
 603:    *
 604:    * @param listener The new listener to add
 605:    */
 606:   public void addMenuKeyListener(MenuKeyListener listener)
 607:   {
 608:     listenerList.add(MenuKeyListener.class, listener);
 609:   }
 610: 
 611:   /**
 612:    * Removes an MenuKeyListener from the menuItem's listener list.
 613:    *
 614:    * @param listener The listener to remove
 615:    */
 616:   public void removeMenuKeyListener(MenuKeyListener listener)
 617:   {
 618:     listenerList.remove(MenuKeyListener.class, listener);
 619:   }
 620: 
 621:   /**
 622:    * Returns all added MenuKeyListener objects.
 623:    *
 624:    * @return an array of listeners
 625:    *
 626:    * @since 1.4
 627:    */
 628:   public MenuKeyListener[] getMenuKeyListeners()
 629:   {
 630:     return (MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class);
 631:   }
 632: 
 633:   /**
 634:    * A string that describes this JMenuItem. Normally only used
 635:    * for debugging.
 636:    *
 637:    * @return A string describing this JMenuItem
 638:    */
 639:   protected String paramString()
 640:   {
 641:     return super.paramString();
 642:   }
 643: 
 644:   public AccessibleContext getAccessibleContext()
 645:   {
 646:     if (accessibleContext == null)
 647:       accessibleContext = new AccessibleJMenuItem();
 648: 
 649:     return accessibleContext;
 650:   }
 651: 
 652:   protected class AccessibleJMenuItem extends AccessibleAbstractButton
 653:     implements ChangeListener
 654:   {
 655:     private static final long serialVersionUID = 6748924232082076534L;
 656: 
 657:     /**
 658:      * Creates a new AccessibleJMenuItem object.
 659:      */
 660:     AccessibleJMenuItem()
 661:     {
 662:       //super(component);
 663:     }
 664: 
 665:     public void stateChanged(ChangeEvent event)
 666:     {
 667:       // TODO: What should be done here, if anything?
 668:     }
 669: 
 670:     public AccessibleRole getAccessibleRole()
 671:     {
 672:       return AccessibleRole.MENU_ITEM;
 673:     }
 674:   }
 675: }