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:    * DOCUMENT ME!
 302:    *
 303:    * @return DOCUMENT ME!
 304:    */
 305:   public JMenu getHelpMenu()
 306:   {
 307:     return null;
 308:   }
 309: 
 310:   /**
 311:    * Returns margin betweeen menu bar's border and its menues
 312:    *
 313:    * @return margin between menu bar's border and its menues
 314:    */
 315:   public Insets getMargin()
 316:   {
 317:     if (margin == null)
 318:       return new Insets(0, 0, 0, 0);
 319:     else
 320:       return margin;
 321:   }
 322: 
 323:   /**
 324:    * Return menu at the specified index. If component at the
 325:    * specified index is not a menu, then null is returned.
 326:    *
 327:    * @param index index to look for the menu
 328:    *
 329:    * @return menu at specified index, or null if menu doesn't exist
 330:    * at the specified index.
 331:    */
 332:   public JMenu getMenu(int index)
 333:   {
 334:     if (getComponentAtIndex(index) instanceof JMenu)
 335:       return (JMenu) getComponentAtIndex(index);
 336:     else
 337:       return null;
 338:   }
 339: 
 340:   /**
 341:    * Returns number of menu's in this menu bar
 342:    *
 343:    * @return number of menu's in this menu bar
 344:    */
 345:   public int getMenuCount()
 346:   {
 347:     return getComponentCount();
 348:   }
 349: 
 350:   /**
 351:    * Returns selection model for this menu bar. SelectionModel
 352:    * keeps track of the selected menu in the menu bar. Whenever
 353:    * selected property of selectionModel changes, the ChangeEvent
 354:    * will be fired its ChangeListeners.
 355:    *
 356:    * @return selection model for this menu bar.
 357:    */
 358:   public SingleSelectionModel getSelectionModel()
 359:   {
 360:     return selectionModel;
 361:   }
 362: 
 363:   /**
 364:    * Method of MenuElement interface. It returns subcomponents
 365:    * of the menu bar, which are all the menues that it contains.
 366:    *
 367:    * @return MenuElement[] array containing menues in this menu bar
 368:    */
 369:   public MenuElement[] getSubElements()
 370:   {
 371:     MenuElement[] subElements = new MenuElement[getComponentCount()];
 372: 
 373:     int j = 0;
 374:     boolean doResize = false;
 375:     MenuElement menu;
 376:     for (int i = 0; i < getComponentCount(); i++)
 377:       {
 378:         menu = getMenu(i);
 379:         if (menu != null)
 380:           {
 381:             subElements[j++] = (MenuElement) menu;
 382:           }
 383:         else
 384:           doResize = true;
 385:       }
 386: 
 387:     if (! doResize)
 388:       return subElements;
 389:     else
 390:       {
 391:         MenuElement[] subElements2 = new MenuElement[j];
 392:         for (int i = 0; i < j; i++)
 393:           subElements2[i] = subElements[i];
 394: 
 395:         return subElements2;
 396:       }
 397:   }
 398: 
 399:   /**
 400:     * Set the "UI" property of the menu bar, which is a look and feel class
 401:     * responsible for handling the menuBar's input events and painting it.
 402:     *
 403:     * @return The current "UI" property
 404:     */
 405:   public MenuBarUI getUI()
 406:   {
 407:     return (MenuBarUI) ui;
 408:   }
 409: 
 410:   /**
 411:    * This method returns a name to identify which look and feel class will be
 412:    * the UI delegate for the menu bar.
 413:    *
 414:    * @return The Look and Feel classID. "MenuBarUI"
 415:    */
 416:   public String getUIClassID()
 417:   {
 418:     return "MenuBarUI";
 419:   }
 420: 
 421:   /**
 422:    * Returns true if menu bar paints its border and false otherwise
 423:    *
 424:    * @return true if menu bar paints its border and false otherwise
 425:    */
 426:   public boolean isBorderPainted()
 427:   {
 428:     return borderPainted;
 429:   }
 430: 
 431:   /**
 432:    * Returns true if some menu in menu bar is selected.
 433:    *
 434:    * @return true if some menu in menu bar is selected and false otherwise
 435:    */
 436:   public boolean isSelected()
 437:   {
 438:     return selectionModel.isSelected();
 439:   }
 440: 
 441:   /**
 442:    * This method does nothing by default. This method is need for the
 443:    * MenuElement interface to be implemented.
 444:    *
 445:    * @param isIncluded true if menuBar is included in the selection
 446:    * and false otherwise
 447:    */
 448:   public void menuSelectionChanged(boolean isIncluded)
 449:   {
 450:     // Do nothing - needed for implementation of MenuElement interface
 451:   }
 452: 
 453:   /**
 454:    * Paints border of the menu bar, if its borderPainted property is set to
 455:    * true.
 456:    *
 457:    * @param g The graphics context with which to paint the border
 458:    */
 459:   protected void paintBorder(Graphics g)
 460:   {
 461:     if (borderPainted)
 462:       {
 463:         Border border = getBorder();
 464:         if (border != null)
 465:           getBorder().paintBorder(this, g, 0, 0, getSize(null).width,
 466:                                   getSize(null).height);
 467:       }
 468:   }
 469: 
 470:   /**
 471:    * A string that describes this JMenuBar. Normally only used
 472:    * for debugging.
 473:    *
 474:    * @return A string describing this JMenuBar
 475:    */
 476:   protected String paramString()
 477:   {
 478:     StringBuffer sb = new StringBuffer();
 479:     sb.append(super.paramString());
 480:     sb.append(",margin=");
 481:     if (getMargin() != null)
 482:       sb.append(getMargin());
 483:     sb.append(",paintBorder=").append(isBorderPainted());
 484:     return sb.toString();
 485:   }
 486: 
 487:   /**
 488:    * Process key events forwarded from MenuSelectionManager. This method
 489:    * doesn't do anything. It is here to conform to the MenuElement interface.
 490:    *
 491:    * @param e event forwarded from MenuSelectionManager
 492:    * @param path path to the menu element from which event was generated
 493:    * @param manager MenuSelectionManager for the current menu hierarchy
 494:    *
 495:    */
 496:   public void processKeyEvent(KeyEvent e, MenuElement[] path,
 497:                               MenuSelectionManager manager)
 498:   {
 499:     // Do nothing - needed for implementation of MenuElement interface
 500:   }
 501: 
 502:   /**
 503:    * This method overrides JComponent.processKeyBinding to allow the 
 504:    * JMenuBar to check all the child components (recursiveley) to see 
 505:    * if they'll consume the event.
 506:    * 
 507:    * @param ks the KeyStroke for the event
 508:    * @param e the KeyEvent for the event
 509:    * @param condition the focus condition for the binding
 510:    * @param pressed true if the key is pressed 
 511:    */
 512:   protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition,
 513:                                       boolean pressed)
 514:   {
 515:     // See if the regular JComponent behavior consumes the event
 516:     if (super.processKeyBinding(ks, e, condition, pressed))
 517:       return true;
 518:     
 519:     // If not, have to recursively check all the child menu elements to see 
 520:     // if they want it    
 521:     MenuElement[] children = getSubElements();
 522:     for (int i = 0; i < children.length; i++)
 523:       if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
 524:         return true;
 525:     return false;
 526:   }
 527:   
 528:   /**
 529:    * This is a helper method to recursively check the children of this
 530:    * JMenuBar to see if they will consume a key event via key bindings.  
 531:    * This is used for menu accelerators.
 532:    * @param menuElement the menuElement to check (and check all its children)
 533:    * @param ks the KeyStroke for the event
 534:    * @param e the KeyEvent that may be consumed
 535:    * @param condition the focus condition for the binding
 536:    * @param pressed true if the key was pressed
 537:    * @return true <code>menuElement</code> or one of its children consume
 538:    * the event (processKeyBinding returns true for menuElement or one of
 539:    * its children).
 540:    */
 541:   static boolean processKeyBindingHelper(MenuElement menuElement, KeyStroke ks,
 542:                                          KeyEvent e, int condition,
 543:                                          boolean pressed)
 544:   {
 545:     if (menuElement == null)
 546:       return false;
 547: 
 548:     // First check the menuElement itself, if it's a JComponent
 549:     if (menuElement instanceof JComponent
 550:         && ((JComponent) menuElement).processKeyBinding(ks, e, condition,
 551:                                                         pressed))
 552:       return true;
 553:     
 554:     // If that didn't consume it, check all the children recursively
 555:     MenuElement[] children = menuElement.getSubElements();
 556:     for (int i = 0; i < children.length; i++)
 557:       if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
 558:         return true;
 559:     return false;
 560:   }
 561:   
 562:   /**
 563:    * Process mouse events forwarded from MenuSelectionManager. This method
 564:    * doesn't do anything. It is here to conform to the MenuElement interface.
 565:    *
 566:    * @param event event forwarded from MenuSelectionManager
 567:    * @param path path to the menu element from which event was generated
 568:    * @param manager MenuSelectionManager for the current menu hierarchy
 569:    *
 570:    */
 571:   public void processMouseEvent(MouseEvent event, MenuElement[] path,
 572:                                 MenuSelectionManager manager)
 573:   {
 574:     // Do nothing - needed for implementation of MenuElement interface
 575:   }
 576: 
 577:   /**
 578:    * This method overrides removeNotify() in the Container to
 579:    * unregister this menu bar from the current keyboard manager.
 580:    */
 581:   public void removeNotify()
 582:   {
 583:     KeyboardManager.getManager().unregisterJMenuBar(this);
 584:     super.removeNotify();
 585:   }
 586: 
 587:   /**
 588:    * Sets painting status of the border. If 'b' is true then menu bar's
 589:    * border will be painted, and it will not be painted otherwise.
 590:    *
 591:    * @param b indicates if menu bar's border should be painted.
 592:    */
 593:   public void setBorderPainted(boolean b)
 594:   {
 595:     if (b != borderPainted)
 596:       {
 597:     boolean old = borderPainted;
 598:     borderPainted = b;
 599:     firePropertyChange("borderPainted", old, b);
 600:     revalidate();
 601:     repaint();
 602:       }
 603:   }
 604: 
 605:   /**
 606:    * Sets help menu for this menu bar
 607:    *
 608:    * @param menu help menu
 609:    *
 610:    * @specnote The specification states that this method is not yet implemented
 611:    *           and should throw an exception.
 612:    */
 613:   public void setHelpMenu(JMenu menu)
 614:   {
 615:     // We throw an Error here, just as Sun's JDK does.
 616:     throw new Error("setHelpMenu() not yet implemented.");
 617:   }
 618: 
 619:   /**
 620:    * Sets the menu bar's "margin" bound property,  which represents
 621:    * distance between the menubar's border and its menus.
 622:    * icon. When marging property is modified, PropertyChangeEvent will
 623:    * be fired to menuBar's PropertyChangeListener's.
 624:    *
 625:    * @param m distance between the menubar's border and its menus.
 626:    *
 627:    */
 628:   public void setMargin(Insets m)
 629:   {
 630:     if (m != margin)
 631:       {
 632:     Insets oldMargin = margin;
 633:     margin = m;
 634:     firePropertyChange("margin", oldMargin, margin);
 635:       }
 636:   }
 637: 
 638:   /**
 639:    * Changes menu bar's selection to the specified menu.
 640:    * This method updates selected index of menu bar's selection model,
 641:    * which results in a model firing change event.
 642:    *
 643:    * @param sel menu to select
 644:    */
 645:   public void setSelected(Component sel)
 646:   {
 647:     int index = getComponentIndex(sel);
 648:     selectionModel.setSelectedIndex(index);
 649:   }
 650: 
 651:   /**
 652:    * Sets menuBar's selection model to the one specified
 653:    *
 654:    * @param model SingleSelectionModel that needs to be set for this menu bar
 655:    */
 656:   public void setSelectionModel(SingleSelectionModel model)
 657:   {
 658:     if (selectionModel != model)
 659:       {
 660:     SingleSelectionModel oldModel = selectionModel;
 661:     selectionModel = model;
 662:     firePropertyChange("model", oldModel, selectionModel);
 663:       }
 664:   }
 665: 
 666:   /**
 667:    * Set the "UI" property of the menu bar, which is a look and feel class
 668:    * responsible for handling menuBar's input events and painting it.
 669:    *
 670:    * @param ui The new "UI" property
 671:    */
 672:   public void setUI(MenuBarUI ui)
 673:   {
 674:     super.setUI(ui);
 675:   }
 676: 
 677:   /**
 678:    * Set the "UI" property to a class constructed, via the {@link
 679:    * UIManager}, from the current look and feel.
 680:    */
 681:   public void updateUI()
 682:   {
 683:     setUI((MenuBarUI) UIManager.getUI(this));
 684:   }
 685: }