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:     KeyStroke old = this.accelerator;
 258:     this.accelerator = keystroke;
 259:     firePropertyChange ("accelerator", old, keystroke);
 260:   }
 261: 
 262:   /**
 263:    * Configures menu items' properties from properties of the specified action.
 264:    * This method overrides configurePropertiesFromAction from AbstractButton
 265:    * to also set accelerator property.
 266:    *
 267:    * @param action action to configure properties from
 268:    */
 269:   protected void configurePropertiesFromAction(Action action)
 270:   {
 271:     super.configurePropertiesFromAction(action);
 272: 
 273:     if (! (this instanceof JMenu) && action != null)
 274:       {
 275:         setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY)));
 276:         super.registerKeyboardAction(action, accelerator, 
 277:                                      JComponent.WHEN_IN_FOCUSED_WINDOW);
 278:       }
 279:   }
 280: 
 281:   /**
 282:    * Creates PropertyChangeListener to listen for the changes in action
 283:    * properties.
 284:    *
 285:    * @param action action to listen to for property changes
 286:    *
 287:    * @return $PropertyChangeListener$ Listener that listens to changes in
 288:    * action properties.
 289:    */
 290:   protected PropertyChangeListener createActionPropertyChangeListener(Action action)
 291:   {
 292:     return new PropertyChangeListener()
 293:       {
 294:     public void propertyChange(PropertyChangeEvent e)
 295:     {
 296:       Action act = (Action) (e.getSource());
 297:       configurePropertiesFromAction(act);
 298:     }
 299:       };
 300:   }
 301: 
 302:   /**
 303:    * Process mouse events forwarded from MenuSelectionManager.
 304:    *
 305:    * @param event event forwarded from MenuSelectionManager
 306:    * @param path path to the menu element from which event was generated
 307:    * @param manager MenuSelectionManager for the current menu hierarchy
 308:    */
 309:   public void processMouseEvent(MouseEvent event, MenuElement[] path,
 310:                                 MenuSelectionManager manager)
 311:   {
 312:     // Fire MenuDragMouseEvents if mouse is being dragged.
 313:     boolean dragged
 314:       = (event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0;
 315:     if (dragged)
 316:       processMenuDragMouseEvent(createMenuDragMouseEvent(event, path, manager));
 317: 
 318:     switch (event.getID())
 319:       {
 320:       case MouseEvent.MOUSE_CLICKED:
 321:     break;
 322:       case MouseEvent.MOUSE_ENTERED:
 323:     if (isRolloverEnabled())
 324:       model.setRollover(true);
 325:     break;
 326:       case MouseEvent.MOUSE_EXITED:
 327:     if (isRolloverEnabled())
 328:       model.setRollover(false);
 329: 
 330:     // for JMenu last element on the path is its popupMenu.
 331:     // JMenu shouldn't me disarmed.    
 332:     if (! (path[path.length - 1] instanceof JPopupMenu) && ! dragged)
 333:       setArmed(false);
 334:     break;
 335:       case MouseEvent.MOUSE_PRESSED:
 336:     if ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0)
 337:       {
 338:         model.setArmed(true);
 339:         model.setPressed(true);
 340:       }
 341:     break;
 342:       case MouseEvent.MOUSE_RELEASED:
 343:     break;
 344:       case MouseEvent.MOUSE_MOVED:
 345:     break;
 346:       case MouseEvent.MOUSE_DRAGGED:
 347:     break;
 348:       }
 349:   }
 350: 
 351:   /**
 352:    * Creates MenuDragMouseEvent.
 353:    *
 354:    * @param event MouseEvent that occured while mouse was pressed.
 355:    * @param path Path the the menu element where the dragging event was
 356:    *        originated
 357:    * @param manager MenuSelectionManager for the current menu hierarchy.
 358:    *
 359:    * @return new MenuDragMouseEvent
 360:    */
 361:   private MenuDragMouseEvent createMenuDragMouseEvent(MouseEvent event,
 362:                                                       MenuElement[] path,
 363:                                                       MenuSelectionManager manager)
 364:   {
 365:     return new MenuDragMouseEvent((Component) event.getSource(),
 366:                                   event.getID(), event.getWhen(),
 367:                                   event.getModifiers(), event.getX(),
 368:                                   event.getY(), event.getClickCount(),
 369:                                   event.isPopupTrigger(), path, manager);
 370:   }
 371: 
 372:   /**
 373:    * Process key events forwarded from MenuSelectionManager.
 374:    *
 375:    * @param event event forwarded from MenuSelectionManager
 376:    * @param path path to the menu element from which event was generated
 377:    * @param manager MenuSelectionManager for the current menu hierarchy
 378:    */
 379:   public void processKeyEvent(KeyEvent event, MenuElement[] path,
 380:                               MenuSelectionManager manager)
 381:   {
 382:     // Need to implement.
 383:   }
 384: 
 385:   /**
 386:    * This method fires MenuDragMouseEvents to registered listeners.
 387:    * Different types of MenuDragMouseEvents are fired depending
 388:    * on the observed mouse event.
 389:    *
 390:    * @param event Mouse
 391:    */
 392:   public void processMenuDragMouseEvent(MenuDragMouseEvent event)
 393:   {
 394:     switch (event.getID())
 395:       {
 396:       case MouseEvent.MOUSE_ENTERED:
 397:     fireMenuDragMouseEntered(event);
 398:     break;
 399:       case MouseEvent.MOUSE_EXITED:
 400:     fireMenuDragMouseExited(event);
 401:     break;
 402:       case MouseEvent.MOUSE_DRAGGED:
 403:     fireMenuDragMouseDragged(event);
 404:     break;
 405:       case MouseEvent.MOUSE_RELEASED:
 406:     fireMenuDragMouseReleased(event);
 407:     break;
 408:       }
 409:   }
 410: 
 411:   /**
 412:    * This method fires MenuKeyEvent to registered listeners.
 413:    * Different types of MenuKeyEvents are fired depending
 414:    * on the observed key event.
 415:    *
 416:    * @param event DOCUMENT ME!
 417:    */
 418:   public void processMenuKeyEvent(MenuKeyEvent event)
 419:   {
 420:     // Need to implement.
 421:   }
 422: 
 423:   /**
 424:    * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
 425:    *
 426:    * @param event The event signifying that mouse entered menuItem while it was dragged
 427:    */
 428:   protected void fireMenuDragMouseEntered(MenuDragMouseEvent event)
 429:   {
 430:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 431: 
 432:     for (int i = 0; i < ll.length; i++)
 433:       ((MenuDragMouseListener) ll[i]).menuDragMouseEntered(event);
 434:   }
 435: 
 436:   /**
 437:    * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
 438:    *
 439:    * @param event The event signifying that mouse has exited menu item, while it was dragged
 440:    */
 441:   protected void fireMenuDragMouseExited(MenuDragMouseEvent event)
 442:   {
 443:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 444: 
 445:     for (int i = 0; i < ll.length; i++)
 446:       ((MenuDragMouseListener) ll[i]).menuDragMouseExited(event);
 447:   }
 448: 
 449:   /**
 450:    * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
 451:    *
 452:    * @param event The event signifying that mouse is being dragged over the menuItem
 453:    */
 454:   protected void fireMenuDragMouseDragged(MenuDragMouseEvent event)
 455:   {
 456:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 457: 
 458:     for (int i = 0; i < ll.length; i++)
 459:       ((MenuDragMouseListener) ll[i]).menuDragMouseDragged(event);
 460:   }
 461: 
 462:   /**
 463:    * This method fires a MenuDragMouseEvent to all the MenuItem's MouseInputListeners.
 464:    *
 465:    * @param event The event signifying that mouse was released while it was dragged over the menuItem
 466:    */
 467:   protected void fireMenuDragMouseReleased(MenuDragMouseEvent event)
 468:   {
 469:     EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
 470: 
 471:     for (int i = 0; i < ll.length; i++)
 472:       ((MenuDragMouseListener) ll[i]).menuDragMouseReleased(event);
 473:   }
 474: 
 475:   /**
 476:    * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
 477:    *
 478:    * @param event The event signifying that key associated with this menu was pressed
 479:    */
 480:   protected void fireMenuKeyPressed(MenuKeyEvent event)
 481:   {
 482:     EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
 483: 
 484:     for (int i = 0; i < ll.length; i++)
 485:       ((MenuKeyListener) ll[i]).menuKeyPressed(event);
 486:   }
 487: 
 488:   /**
 489:    * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
 490:    *
 491:    * @param event The event signifying that key associated with this menu was released
 492:    */
 493:   protected void fireMenuKeyReleased(MenuKeyEvent event)
 494:   {
 495:     EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
 496: 
 497:     for (int i = 0; i < ll.length; i++)
 498:       ((MenuKeyListener) ll[i]).menuKeyTyped(event);
 499:   }
 500: 
 501:   /**
 502:    * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
 503:    *
 504:    * @param event The event signifying that key associated with this menu was typed.
 505:    *        The key is typed when it was pressed and then released
 506:    */
 507:   protected void fireMenuKeyTyped(MenuKeyEvent event)
 508:   {
 509:     EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
 510: 
 511:     for (int i = 0; i < ll.length; i++)
 512:       ((MenuKeyListener) ll[i]).menuKeyTyped(event);
 513:   }
 514: 
 515:   /**
 516:    * Method of the MenuElement interface.
 517:    * This method is invoked by MenuSelectionManager when selection of
 518:    * this menu item has changed. If this menu item was selected then
 519:    * arm it's model, and disarm the model otherwise. The menu item
 520:    * is considered to be selected, and thus highlighted when its model
 521:    * is armed.
 522:    *
 523:    * @param changed indicates selection status of this menu item. If changed is
 524:    * true then menu item is selected and deselected otherwise.
 525:    */
 526:   public void menuSelectionChanged(boolean changed)
 527:   {
 528:     Component parent = this.getParent();
 529:     if (changed)
 530:       {
 531:     model.setArmed(true);
 532: 
 533:     if (parent != null && parent instanceof JPopupMenu)
 534:       ((JPopupMenu) parent).setSelected(this);
 535:       }
 536:     else
 537:       {
 538:     model.setArmed(false);
 539: 
 540:     if (parent != null && parent instanceof JPopupMenu)
 541:       ((JPopupMenu) parent).getSelectionModel().clearSelection();
 542:       }
 543:   }
 544: 
 545:   /**
 546:    * Method of the MenuElement interface.
 547:    *
 548:    * @return $MenuElement[]$ Returns array of sub-components for this menu
 549:    *         item. By default menuItem doesn't have any subcomponents and so
 550:    *         empty array is returned instead.
 551:    */
 552:   public MenuElement[] getSubElements()
 553:   {
 554:     return new MenuElement[0];
 555:   }
 556: 
 557:   /**
 558:    * Returns reference to the component that will paint this menu item.
 559:    *
 560:    * @return $Component$ Component that will paint this menu item.
 561:    *         Simply returns reference to this menu item.
 562:    */
 563:   public Component getComponent()
 564:   {
 565:     return this;
 566:   }
 567: 
 568:   /**
 569:    * Adds a MenuDragMouseListener to this menu item. When mouse
 570:    * is dragged over the menu item the MenuDragMouseEvents will be
 571:    * fired, and these listeners will be called.
 572:    *
 573:    * @param listener The new listener to add
 574:    */
 575:   public void addMenuDragMouseListener(MenuDragMouseListener listener)
 576:   {
 577:     listenerList.add(MenuDragMouseListener.class, listener);
 578:   }
 579: 
 580:   /**
 581:    * Removes a MenuDragMouseListener from the menuItem's listener list.
 582:    *
 583:    * @param listener The listener to remove
 584:    */
 585:   public void removeMenuDragMouseListener(MenuDragMouseListener listener)
 586:   {
 587:     listenerList.remove(MenuDragMouseListener.class, listener);
 588:   }
 589: 
 590:   /**
 591:    * Returns all added MenuDragMouseListener objects.
 592:    *
 593:    * @return an array of listeners
 594:    *
 595:    * @since 1.4
 596:    */
 597:   public MenuDragMouseListener[] getMenuDragMouseListeners()
 598:   {
 599:     return (MenuDragMouseListener[]) listenerList.getListeners(MenuDragMouseListener.class);
 600:   }
 601: 
 602:   /**
 603:    * Adds an MenuKeyListener to this menu item.  This listener will be
 604:    * invoked when MenuKeyEvents will be fired by this menu item.
 605:    *
 606:    * @param listener The new listener to add
 607:    */
 608:   public void addMenuKeyListener(MenuKeyListener listener)
 609:   {
 610:     listenerList.add(MenuKeyListener.class, listener);
 611:   }
 612: 
 613:   /**
 614:    * Removes an MenuKeyListener from the menuItem's listener list.
 615:    *
 616:    * @param listener The listener to remove
 617:    */
 618:   public void removeMenuKeyListener(MenuKeyListener listener)
 619:   {
 620:     listenerList.remove(MenuKeyListener.class, listener);
 621:   }
 622: 
 623:   /**
 624:    * Returns all added MenuKeyListener objects.
 625:    *
 626:    * @return an array of listeners
 627:    *
 628:    * @since 1.4
 629:    */
 630:   public MenuKeyListener[] getMenuKeyListeners()
 631:   {
 632:     return (MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class);
 633:   }
 634: 
 635:   /**
 636:    * A string that describes this JMenuItem. Normally only used
 637:    * for debugging.
 638:    *
 639:    * @return A string describing this JMenuItem
 640:    */
 641:   protected String paramString()
 642:   {
 643:     return super.paramString();
 644:   }
 645: 
 646:   public AccessibleContext getAccessibleContext()
 647:   {
 648:     if (accessibleContext == null)
 649:       accessibleContext = new AccessibleJMenuItem();
 650: 
 651:     return accessibleContext;
 652:   }
 653: 
 654:   protected class AccessibleJMenuItem extends AccessibleAbstractButton
 655:     implements ChangeListener
 656:   {
 657:     private static final long serialVersionUID = 6748924232082076534L;
 658: 
 659:     /**
 660:      * Creates a new AccessibleJMenuItem object.
 661:      */
 662:     AccessibleJMenuItem()
 663:     {
 664:       //super(component);
 665:     }
 666: 
 667:     public void stateChanged(ChangeEvent event)
 668:     {
 669:       // TODO: What should be done here, if anything?
 670:     }
 671: 
 672:     public AccessibleRole getAccessibleRole()
 673:     {
 674:       return AccessibleRole.MENU_ITEM;
 675:     }
 676:   }
 677: }