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