Source for javax.swing.JMenuBar

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