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