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