Source for javax.swing.JMenuBar

   1: /* JMenuBar.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.Graphics;
  43: import java.awt.Insets;
  44: import java.awt.event.KeyEvent;
  45: import java.awt.event.MouseEvent;
  46: 
  47: import javax.accessibility.Accessible;
  48: import javax.accessibility.AccessibleContext;
  49: import javax.accessibility.AccessibleRole;
  50: import javax.accessibility.AccessibleSelection;
  51: import javax.accessibility.AccessibleStateSet;
  52: import javax.swing.plaf.MenuBarUI;
  53: 
  54: /**
  55:  * JMenuBar is a container for menu's. For a menu bar to be seen on the
  56:  * screen, at least one menu should be added to it. Just like adding
  57:  * components to container, one can use add() to add menu's to the menu bar.
  58:  * Menu's will be displayed in the menu  bar in the order they were added.
  59:  * The JMenuBar uses selectionModel to keep track of selected menu index.
  60:  * JMenuBar's selectionModel will fire ChangeEvents to its registered 
  61:  * listeners when the selected index changes.
  62:  */
  63: public class JMenuBar extends JComponent implements Accessible, MenuElement
  64: {
  65:   /**
  66:    * Provides accessibility support for <code>JMenuBar</code>.
  67:    * 
  68:    * @author Roman Kennke (kennke@aicas.com)
  69:    */
  70:   protected class AccessibleJMenuBar extends AccessibleJComponent
  71:     implements AccessibleSelection
  72:   {
  73: 
  74:     /**
  75:      * Returns the number of selected items in the menu bar. Possible values
  76:      * are <code>0</code> if nothing is selected, or <code>1</code> if one
  77:      * item is selected.
  78:      *
  79:      * @return the number of selected items in the menu bar
  80:      */
  81:     public int getAccessibleSelectionCount()
  82:     {
  83:       int count = 0;
  84:       if (getSelectionModel().getSelectedIndex() != -1)
  85:         count = 1;
  86:       return count;
  87:     }
  88: 
  89:     /**
  90:      * Returns the selected with index <code>i</code> menu, or
  91:      * <code>null</code> if the specified menu is not selected.
  92:      *
  93:      * @param i the index of the menu to return
  94:      *
  95:      * @return the selected with index <code>i</code> menu, or
  96:      *         <code>null</code> if the specified menu is not selected
  97:      */
  98:     public Accessible getAccessibleSelection(int i)
  99:     {
 100:       if (getSelectionModel().getSelectedIndex() != i)
 101:         return null;
 102:       return getMenu(i);
 103:     }
 104: 
 105:     /**
 106:      * Returns <code>true</code> if the specified menu is selected,
 107:      * <code>false</code> otherwise.
 108:      *
 109:      * @param i the index of the menu to check
 110:      *
 111:      *@return <code>true</code> if the specified menu is selected,
 112:      *        <code>false</code> otherwise
 113:      */
 114:     public boolean isAccessibleChildSelected(int i)
 115:     {
 116:       return getSelectionModel().getSelectedIndex() == i;
 117:     }
 118: 
 119:     /**
 120:      * Selects the menu with index <code>i</code>. If another menu is already
 121:      * selected, this will be deselected.
 122:      *
 123:      * @param i the menu to be selected
 124:      */
 125:     public void addAccessibleSelection(int i)
 126:     {
 127:       getSelectionModel().setSelectedIndex(i);
 128:     }
 129: 
 130:     /**
 131:      * Deselects the menu with index <code>i</code>.
 132:      *
 133:      * @param i the menu index to be deselected
 134:      */
 135:     public void removeAccessibleSelection(int i)
 136:     {
 137:       if (getSelectionModel().getSelectedIndex() == i)
 138:         getSelectionModel().clearSelection();
 139:     }
 140: 
 141:     /**
 142:      * Deselects all possibly selected menus.
 143:      */
 144:     public void clearAccessibleSelection()
 145:     {
 146:       getSelectionModel().clearSelection();
 147:     }
 148: 
 149:     /**
 150:      * In menu bars it is not possible to select all items, so this method
 151:      * does nothing.
 152:      */
 153:     public void selectAllAccessibleSelection()
 154:     {
 155:       // In menu bars it is not possible to select all items, so this method
 156:       // does nothing.
 157:     }
 158: 
 159:     /**
 160:      * Returns the accessible role of <code>JMenuBar</code>, which is
 161:      * {@link AccessibleRole#MENU_BAR}.
 162:      *
 163:      * @return the accessible role of <code>JMenuBar</code>, which is
 164:      *         {@link AccessibleRole#MENU_BAR}
 165:      */
 166:     public AccessibleRole getAccessibleRole()
 167:     {
 168:       return AccessibleRole.MENU_BAR;
 169:     }
 170: 
 171:     /**
 172:      * Returns the <code>AccessibleSelection</code> for this object. This
 173:      * method returns <code>this</code>, since the
 174:      * <code>AccessibleJMenuBar</code> manages its selection itself.
 175:      *
 176:      * @return the <code>AccessibleSelection</code> for this object
 177:      */
 178:     public AccessibleSelection getAccessibleSelection()
 179:     {
 180:       return this;
 181:     }
 182: 
 183:     /**
 184:      * Returns the state of this <code>AccessibleJMenuBar</code>.
 185:      *
 186:      * @return the state of this <code>AccessibleJMenuBar</code>.
 187:      */
 188:     public AccessibleStateSet getAccessibleStateSet()
 189:     {
 190:       AccessibleStateSet stateSet = super.getAccessibleStateSet();
 191:       // TODO: Figure out what state must be added to the super state set.
 192:       return stateSet;
 193:     }
 194:   }
 195: 
 196:   private static final long serialVersionUID = -8191026883931977036L;
 197: 
 198:   /** JMenuBar's model. It keeps track of selected menu's index */
 199:   private transient SingleSelectionModel selectionModel;
 200: 
 201:   /* borderPainted property indicating if the menuBar's border will be painted*/
 202:   private boolean borderPainted;
 203: 
 204:   /* margin between menu bar's border and its menues*/
 205:   private Insets margin;
 206: 
 207:   /**
 208:    * Creates a new JMenuBar object.
 209:    */
 210:   public JMenuBar()
 211:   {
 212:     selectionModel = new DefaultSingleSelectionModel();
 213:     borderPainted = true;
 214:     updateUI();
 215:   }
 216: 
 217:   /**
 218:    * Adds menu to the menu bar
 219:    *
 220:    * @param c menu to add
 221:    *
 222:    * @return reference to the added menu
 223:    */
 224:   public JMenu add(JMenu c)
 225:   {
 226:     c.setAlignmentX(Component.LEFT_ALIGNMENT);
 227:     super.add(c);
 228:     return c;
 229:   }
 230: 
 231:   /**
 232:    * This method overrides addNotify() in the Container to register
 233:    * this menu bar with the current keyboard manager.
 234:    */
 235:   public void addNotify()
 236:   {
 237:     super.addNotify();
 238:     KeyboardManager.getManager().registerJMenuBar(this);
 239:   }
 240: 
 241:   public AccessibleContext getAccessibleContext()
 242:   {
 243:     if (accessibleContext == null)
 244:       accessibleContext = new AccessibleJMenuBar();
 245:     return accessibleContext;
 246:   }
 247: 
 248:   /**
 249:    * Returns reference to this menu bar
 250:    *
 251:    * @return reference to this menu bar
 252:    */
 253:   public Component getComponent()
 254:   {
 255:     return this;
 256:   }
 257: 
 258:   /**
 259:    * Returns component at the specified index.
 260:    *
 261:    * @param i index of the component to get
 262:    *
 263:    * @return component at the specified index. Null is returned if
 264:    * component at the specified index doesn't exist.
 265:    * @deprecated Replaced by getComponent(int)
 266:    */
 267:   public Component getComponentAtIndex(int i)
 268:   {
 269:     return getComponent(i);
 270:   }
 271: 
 272:   /**
 273:    * Returns index of the specified component
 274:    *
 275:    * @param c Component to search for
 276:    *
 277:    * @return index of the specified component. -1 is returned if
 278:    * specified component doesnt' exist in the menu bar.
 279:    */
 280:   public int getComponentIndex(Component c)
 281:   {
 282:     Component[] comps = getComponents();
 283: 
 284:     int index = -1;
 285: 
 286:     for (int i = 0; i < comps.length; i++)
 287:       {
 288:     if (comps[i].equals(c))
 289:       {
 290:         index = i;
 291:         break;
 292:       }
 293:       }
 294: 
 295:     return index;
 296:   }
 297: 
 298:   /**
 299:    * DOCUMENT ME!
 300:    *
 301:    * @return DOCUMENT ME!
 302:    */
 303:   public JMenu getHelpMenu()
 304:   {
 305:     return null;
 306:   }
 307: 
 308:   /**
 309:    * Returns margin betweeen menu bar's border and its menues
 310:    *
 311:    * @return margin between menu bar's border and its menues
 312:    */
 313:   public Insets getMargin()
 314:   {
 315:     if (margin == null)
 316:       return new Insets(0, 0, 0, 0);
 317:     else
 318:       return margin;
 319:   }
 320: 
 321:   /**
 322:    * Return menu at the specified index. If component at the
 323:    * specified index is not a menu, then null is returned.
 324:    *
 325:    * @param index index to look for the menu
 326:    *
 327:    * @return menu at specified index, or null if menu doesn't exist
 328:    * at the specified index.
 329:    */
 330:   public JMenu getMenu(int index)
 331:   {
 332:     if (getComponentAtIndex(index) instanceof JMenu)
 333:       return (JMenu) getComponentAtIndex(index);
 334:     else
 335:       return null;
 336:   }
 337: 
 338:   /**
 339:    * Returns number of menu's in this menu bar
 340:    *
 341:    * @return number of menu's in this menu bar
 342:    */
 343:   public int getMenuCount()
 344:   {
 345:     return getComponentCount();
 346:   }
 347: 
 348:   /**
 349:    * Returns selection model for this menu bar. SelectionModel
 350:    * keeps track of the selected menu in the menu bar. Whenever
 351:    * selected property of selectionModel changes, the ChangeEvent
 352:    * will be fired its ChangeListeners.
 353:    *
 354:    * @return selection model for this menu bar.
 355:    */
 356:   public SingleSelectionModel getSelectionModel()
 357:   {
 358:     return selectionModel;
 359:   }
 360: 
 361:   /**
 362:    * Method of MenuElement interface. It returns subcomponents
 363:    * of the menu bar, which are all the menues that it contains.
 364:    *
 365:    * @return MenuElement[] array containing menues in this menu bar
 366:    */
 367:   public MenuElement[] getSubElements()
 368:   {
 369:     MenuElement[] subElements = new MenuElement[getComponentCount()];
 370: 
 371:     for (int i = 0; i < getComponentCount(); i++)
 372:       subElements[i] = (MenuElement) getMenu(i);
 373: 
 374:     return subElements;
 375:   }
 376: 
 377:   /**
 378:     * Set the "UI" property of the menu bar, which is a look and feel class
 379:     * responsible for handling the menuBar's input events and painting it.
 380:     *
 381:     * @return The current "UI" property
 382:     */
 383:   public MenuBarUI getUI()
 384:   {
 385:     return (MenuBarUI) ui;
 386:   }
 387: 
 388:   /**
 389:    * This method returns a name to identify which look and feel class will be
 390:    * the UI delegate for the menu bar.
 391:    *
 392:    * @return The Look and Feel classID. "MenuBarUI"
 393:    */
 394:   public String getUIClassID()
 395:   {
 396:     return "MenuBarUI";
 397:   }
 398: 
 399:   /**
 400:    * Returns true if menu bar paints its border and false otherwise
 401:    *
 402:    * @return true if menu bar paints its border and false otherwise
 403:    */
 404:   public boolean isBorderPainted()
 405:   {
 406:     return borderPainted;
 407:   }
 408: 
 409:   /**
 410:    * Returns true if some menu in menu bar is selected.
 411:    *
 412:    * @return true if some menu in menu bar is selected and false otherwise
 413:    */
 414:   public boolean isSelected()
 415:   {
 416:     return selectionModel.isSelected();
 417:   }
 418: 
 419:   /**
 420:    * This method does nothing by default. This method is need for the
 421:    * MenuElement interface to be implemented.
 422:    *
 423:    * @param isIncluded true if menuBar is included in the selection
 424:    * and false otherwise
 425:    */
 426:   public void menuSelectionChanged(boolean isIncluded)
 427:   {
 428:     // Do nothing - needed for implementation of MenuElement interface
 429:   }
 430: 
 431:   /**
 432:    * Paints border of the menu bar, if its borderPainted property is set to
 433:    * true.
 434:    *
 435:    * @param g The graphics context with which to paint the border
 436:    */
 437:   protected void paintBorder(Graphics g)
 438:   {
 439:     if (borderPainted)
 440:       getBorder().paintBorder(this, g, 0, 0, getSize(null).width,
 441:                               getSize(null).height);
 442:   }
 443: 
 444:   /**
 445:    * A string that describes this JMenuBar. Normally only used
 446:    * for debugging.
 447:    *
 448:    * @return A string describing this JMenuBar
 449:    */
 450:   protected String paramString()
 451:   {
 452:     StringBuffer sb = new StringBuffer();
 453:     sb.append(super.paramString());
 454:     sb.append(",margin=");
 455:     if (getMargin() != null)
 456:       sb.append(getMargin());
 457:     sb.append(",paintBorder=").append(isBorderPainted());
 458:     return sb.toString();
 459:   }
 460: 
 461:   /**
 462:    * Process key events forwarded from MenuSelectionManager. This method
 463:    * doesn't do anything. It is here to conform to the MenuElement interface.
 464:    *
 465:    * @param e event forwarded from MenuSelectionManager
 466:    * @param path path to the menu element from which event was generated
 467:    * @param manager MenuSelectionManager for the current menu hierarchy
 468:    *
 469:    */
 470:   public void processKeyEvent(KeyEvent e, MenuElement[] path,
 471:                               MenuSelectionManager manager)
 472:   {
 473:     // Do nothing - needed for implementation of MenuElement interface
 474:   }
 475: 
 476:   /**
 477:    * This method overrides JComponent.processKeyBinding to allow the 
 478:    * JMenuBar to check all the child components (recursiveley) to see 
 479:    * if they'll consume the event.
 480:    * 
 481:    * @param ks the KeyStroke for the event
 482:    * @param e the KeyEvent for the event
 483:    * @param condition the focus condition for the binding
 484:    * @param pressed true if the key is pressed 
 485:    */
 486:   protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition,
 487:                                       boolean pressed)
 488:   {
 489:     // See if the regular JComponent behavior consumes the event
 490:     if (super.processKeyBinding(ks, e, condition, pressed))
 491:       return true;
 492:     
 493:     // If not, have to recursively check all the child menu elements to see 
 494:     // if they want it    
 495:     MenuElement[] children = getSubElements();
 496:     for (int i = 0; i < children.length; i++)
 497:       if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
 498:         return true;
 499:     return false;
 500:   }
 501:   
 502:   /**
 503:    * This is a helper method to recursively check the children of this
 504:    * JMenuBar to see if they will consume a key event via key bindings.  
 505:    * This is used for menu accelerators.
 506:    * @param menuElement the menuElement to check (and check all its children)
 507:    * @param ks the KeyStroke for the event
 508:    * @param e the KeyEvent that may be consumed
 509:    * @param condition the focus condition for the binding
 510:    * @param pressed true if the key was pressed
 511:    * @return true <code>menuElement</code> or one of its children consume
 512:    * the event (processKeyBinding returns true for menuElement or one of
 513:    * its children).
 514:    */
 515:   static boolean processKeyBindingHelper(MenuElement menuElement, KeyStroke ks,
 516:                                          KeyEvent e, int condition,
 517:                                          boolean pressed)
 518:   {
 519:     // First check the menuElement itself, if it's a JComponent
 520:     if (menuElement instanceof JComponent
 521:         && ((JComponent) menuElement).processKeyBinding(ks, e, condition,
 522:                                                         pressed))
 523:       return true;
 524:     
 525:     // If that didn't consume it, check all the children recursively
 526:     MenuElement[] children = menuElement.getSubElements();
 527:     for (int i = 0; i < children.length; i++)
 528:       if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
 529:         return true;
 530:     return false;
 531:   }
 532:   
 533:   /**
 534:    * Process mouse events forwarded from MenuSelectionManager. This method
 535:    * doesn't do anything. It is here to conform to the MenuElement interface.
 536:    *
 537:    * @param event event forwarded from MenuSelectionManager
 538:    * @param path path to the menu element from which event was generated
 539:    * @param manager MenuSelectionManager for the current menu hierarchy
 540:    *
 541:    */
 542:   public void processMouseEvent(MouseEvent event, MenuElement[] path,
 543:                                 MenuSelectionManager manager)
 544:   {
 545:     // Do nothing - needed for implementation of MenuElement interface
 546:   }
 547: 
 548:   /**
 549:    * This method overrides removeNotify() in the Container to
 550:    * unregister this menu bar from the current keyboard manager.
 551:    */
 552:   public void removeNotify()
 553:   {
 554:     KeyboardManager.getManager().unregisterJMenuBar(this);
 555:     super.removeNotify();
 556:   }
 557: 
 558:   /**
 559:    * Sets painting status of the border. If 'b' is true then menu bar's
 560:    * border will be painted, and it will not be painted otherwise.
 561:    *
 562:    * @param b indicates if menu bar's border should be painted.
 563:    */
 564:   public void setBorderPainted(boolean b)
 565:   {
 566:     if (b != borderPainted)
 567:       {
 568:     boolean old = borderPainted;
 569:     borderPainted = b;
 570:     firePropertyChange("borderPainted", old, b);
 571:     revalidate();
 572:     repaint();
 573:       }
 574:   }
 575: 
 576:   /**
 577:    * Sets help menu for this menu bar
 578:    *
 579:    * @param menu help menu
 580:    *
 581:    * @specnote The specification states that this method is not yet implemented
 582:    *           and should throw an exception.
 583:    */
 584:   public void setHelpMenu(JMenu menu)
 585:   {
 586:     // We throw an Error here, just as Sun's JDK does.
 587:     throw new Error("setHelpMenu() not yet implemented.");
 588:   }
 589: 
 590:   /**
 591:    * Sets the menu bar's "margin" bound property,  which represents
 592:    * distance between the menubar's border and its menus.
 593:    * icon. When marging property is modified, PropertyChangeEvent will
 594:    * be fired to menuBar's PropertyChangeListener's.
 595:    *
 596:    * @param m distance between the menubar's border and its menus.
 597:    *
 598:    */
 599:   public void setMargin(Insets m)
 600:   {
 601:     if (m != margin)
 602:       {
 603:     Insets oldMargin = margin;
 604:     margin = m;
 605:     firePropertyChange("margin", oldMargin, margin);
 606:       }
 607:   }
 608: 
 609:   /**
 610:    * Changes menu bar's selection to the specified menu.
 611:    * This method updates selected index of menu bar's selection model,
 612:    * which results in a model firing change event.
 613:    *
 614:    * @param sel menu to select
 615:    */
 616:   public void setSelected(Component sel)
 617:   {
 618:     int index = getComponentIndex(sel);
 619:     selectionModel.setSelectedIndex(index);
 620:   }
 621: 
 622:   /**
 623:    * Sets menuBar's selection model to the one specified
 624:    *
 625:    * @param model SingleSelectionModel that needs to be set for this menu bar
 626:    */
 627:   public void setSelectionModel(SingleSelectionModel model)
 628:   {
 629:     if (selectionModel != model)
 630:       {
 631:     SingleSelectionModel oldModel = selectionModel;
 632:     selectionModel = model;
 633:     firePropertyChange("model", oldModel, selectionModel);
 634:       }
 635:   }
 636: 
 637:   /**
 638:    * Set the "UI" property of the menu bar, which is a look and feel class
 639:    * responsible for handling menuBar's input events and painting it.
 640:    *
 641:    * @param ui The new "UI" property
 642:    */
 643:   public void setUI(MenuBarUI ui)
 644:   {
 645:     super.setUI(ui);
 646:   }
 647: 
 648:   /**
 649:    * Set the "UI" property to a class constructed, via the {@link
 650:    * UIManager}, from the current look and feel.
 651:    */
 652:   public void updateUI()
 653:   {
 654:     setUI((MenuBarUI) UIManager.getUI(this));
 655:     invalidate();
 656:   }
 657: }