Frames | No Frames |
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: }