/* * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing; import java.awt.Component; import java.awt.Graphics; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.*; import java.beans.JavaBean; import java.beans.BeanProperty; import java.beans.Transient; import java.util.Vector; import java.io.Serializable; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import javax.swing.plaf.*; import javax.accessibility.*; import sun.awt.SunToolkit; /** * An implementation of a menu bar. You add JMenu objects to the * menu bar to construct a menu. When the user selects a JMenu * object, its associated JPopupMenu is displayed, allowing the * user to select one of the JMenuItems on it. *

* For information and examples of using menu bars see * How to Use Menus, * a section in The Java Tutorial. *

* Warning: Swing is not thread safe. For more * information see Swing's Threading * Policy. *

* Warning: * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. *

* Warning: * By default, pressing the Tab key does not transfer focus from a * JMenuBar which is added to a container together with other Swing * components, because the focusTraversalKeysEnabled property * of JMenuBar is set to false. To resolve this, * you should call the JMenuBar.setFocusTraversalKeysEnabled(true) * method. * * @author Georges Saab * @author David Karlton * @author Arnaud Weber * @see JMenu * @see JPopupMenu * @see JMenuItem * @since 1.2 */ @JavaBean(defaultProperty = "UI", description = "A container for holding and displaying menus.") @SwingContainer @SuppressWarnings("serial") public class JMenuBar extends JComponent implements Accessible,MenuElement { /** * @see #getUIClassID * @see #readObject */ private static final String uiClassID = "MenuBarUI"; /* * Model for the selected subcontrol. */ private transient SingleSelectionModel selectionModel; private boolean paintBorder = true; private Insets margin = null; /* diagnostic aids -- should be false for production builds. */ private static final boolean TRACE = false; // trace creates and disposes private static final boolean VERBOSE = false; // show reuse hits/misses private static final boolean DEBUG = false; // show bad params, misc. /** * Creates a new menu bar. */ public JMenuBar() { super(); setFocusTraversalKeysEnabled(false); setSelectionModel(new DefaultSingleSelectionModel()); updateUI(); } /** * Returns the menubar's current UI. * * @return a {@code MenuBarUI} which is the menubar's current L&F object * @see #setUI */ public MenuBarUI getUI() { return (MenuBarUI)ui; } /** * Sets the L&F object that renders this component. * * @param ui the new MenuBarUI L&F object * @see UIDefaults#getUI */ @BeanProperty(hidden = true, visualUpdate = true, description = "The UI object that implements the Component's LookAndFeel.") public void setUI(MenuBarUI ui) { super.setUI(ui); } /** * Resets the UI property with a value from the current look and feel. * * @see JComponent#updateUI */ public void updateUI() { Toolkit tk = Toolkit.getDefaultToolkit(); if (tk instanceof SunToolkit) { ((SunToolkit)tk).updateScreenMenuBarUI(); } setUI((MenuBarUI)UIManager.getUI(this)); } /** * Returns the name of the L&F class that renders this component. * * @return the string "MenuBarUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */ @BeanProperty(bound = false) public String getUIClassID() { return uiClassID; } /** * Returns the model object that handles single selections. * * @return the SingleSelectionModel property * @see SingleSelectionModel */ public SingleSelectionModel getSelectionModel() { return selectionModel; } /** * Sets the model object to handle single selections. * * @param model the SingleSelectionModel to use * @see SingleSelectionModel */ @BeanProperty(description = "The selection model, recording which child is selected.") public void setSelectionModel(SingleSelectionModel model) { SingleSelectionModel oldValue = selectionModel; this.selectionModel = model; firePropertyChange("selectionModel", oldValue, selectionModel); } /** * Appends the specified menu to the end of the menu bar. * * @param c the JMenu component to add * @return the menu component */ public JMenu add(JMenu c) { super.add(c); return c; } /** * Returns the menu at the specified position in the menu bar. * * @param index an integer giving the position in the menu bar, where * 0 is the first position * @return the JMenu at that position, or null if * if there is no JMenu at that position (ie. if * it is a JMenuItem) */ public JMenu getMenu(int index) { Component c = getComponentAtIndex(index); if (c instanceof JMenu) return (JMenu) c; return null; } /** * Returns the number of items in the menu bar. * * @return the number of items in the menu bar */ @BeanProperty(bound = false) public int getMenuCount() { return getComponentCount(); } /** * Sets the help menu that appears when the user selects the * "help" option in the menu bar. This method is not yet implemented * and will throw an exception. * * @param menu the JMenu that delivers help to the user */ public void setHelpMenu(JMenu menu) { throw new Error("setHelpMenu() not yet implemented."); } /** * Gets the help menu for the menu bar. This method is not yet * implemented and will throw an exception. * * @return the JMenu that delivers help to the user */ @Transient public JMenu getHelpMenu() { throw new Error("getHelpMenu() not yet implemented."); } /** * Returns the component at the specified index. * * @param i an integer specifying the position, where 0 is first * @return the Component at the position, * or null for an invalid index * @deprecated replaced by getComponent(int i) */ @Deprecated public Component getComponentAtIndex(int i) { if(i < 0 || i >= getComponentCount()) { return null; } return getComponent(i); } /** * Returns the index of the specified component. * * @param c the Component to find * @return an integer giving the component's position, where 0 is first; * or -1 if it can't be found */ public int getComponentIndex(Component c) { int ncomponents = this.getComponentCount(); Component[] component = this.getComponents(); for (int i = 0 ; i < ncomponents ; i++) { Component comp = component[i]; if (comp == c) return i; } return -1; } /** * Sets the currently selected component, producing a * a change to the selection model. * * @param sel the Component to select */ public void setSelected(Component sel) { SingleSelectionModel model = getSelectionModel(); int index = getComponentIndex(sel); model.setSelectedIndex(index); } /** * Returns true if the menu bar currently has a component selected. * * @return true if a selection has been made, else false */ @BeanProperty(bound = false) public boolean isSelected() { return selectionModel.isSelected(); } /** * Returns true if the menu bars border should be painted. * * @return true if the border should be painted, else false */ public boolean isBorderPainted() { return paintBorder; } /** * Sets whether the border should be painted. * * @param b if true and border property is not null, * the border is painted. * @see #isBorderPainted */ @BeanProperty(visualUpdate = true, description = "Whether the border should be painted.") public void setBorderPainted(boolean b) { boolean oldValue = paintBorder; paintBorder = b; firePropertyChange("borderPainted", oldValue, paintBorder); if (b != oldValue) { revalidate(); repaint(); } } /** * Paints the menubar's border if BorderPainted * property is true. * * @param g the Graphics context to use for painting * @see JComponent#paint * @see JComponent#setBorder */ protected void paintBorder(Graphics g) { if (isBorderPainted()) { super.paintBorder(g); } } /** * Sets the margin between the menubar's border and * its menus. Setting to null will cause the menubar to * use the default margins. * * @param m an Insets object containing the margin values * @see Insets */ @BeanProperty(visualUpdate = true, description = "The space between the menubar's border and its contents") public void setMargin(Insets m) { Insets old = margin; this.margin = m; firePropertyChange("margin", old, m); if (old == null || !old.equals(m)) { revalidate(); repaint(); } } /** * Returns the margin between the menubar's border and * its menus. If there is no previous margin, it will create * a default margin with zero size. * * @return an Insets object containing the margin values * @see Insets */ public Insets getMargin() { if(margin == null) { return new Insets(0,0,0,0); } else { return margin; } } /** * Implemented to be a MenuElement -- does nothing. * * @see #getSubElements */ public void processMouseEvent(MouseEvent event,MenuElement[] path,MenuSelectionManager manager) { } /** * Implemented to be a MenuElement -- does nothing. * * @see #getSubElements */ public void processKeyEvent(KeyEvent e,MenuElement[] path,MenuSelectionManager manager) { } /** * Implemented to be a MenuElement -- does nothing. * * @see #getSubElements */ public void menuSelectionChanged(boolean isIncluded) { } /** * Implemented to be a MenuElement -- returns the * menus in this menu bar. * This is the reason for implementing the MenuElement * interface -- so that the menu bar can be treated the same as * other menu elements. * @return an array of menu items in the menu bar. */ @BeanProperty(bound = false) public MenuElement[] getSubElements() { MenuElement[] result; Vector tmp = new Vector(); int c = getComponentCount(); int i; Component m; for(i=0 ; i < c ; i++) { m = getComponent(i); if(m instanceof MenuElement) tmp.addElement((MenuElement) m); } result = new MenuElement[tmp.size()]; for(i=0,c=tmp.size() ; i < c ; i++) result[i] = tmp.elementAt(i); return result; } /** * Implemented to be a MenuElement. Returns this object. * * @return the current Component (this) * @see #getSubElements */ public Component getComponent() { return this; } /** * Returns a string representation of this JMenuBar. * This method * is intended to be used only for debugging purposes, and the * content and format of the returned string may vary between * implementations. The returned string may be empty but may not * be null. * * @return a string representation of this JMenuBar */ protected String paramString() { String paintBorderString = (paintBorder ? "true" : "false"); String marginString = (margin != null ? margin.toString() : ""); return super.paramString() + ",margin=" + marginString + ",paintBorder=" + paintBorderString; } ///////////////// // Accessibility support //////////////// /** * Gets the AccessibleContext associated with this JMenuBar. * For JMenuBars, the AccessibleContext takes the form of an * AccessibleJMenuBar. * A new AccessibleJMenuBar instance is created if necessary. * * @return an AccessibleJMenuBar that serves as the * AccessibleContext of this JMenuBar */ @BeanProperty(bound = false) public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessibleJMenuBar(); } return accessibleContext; } /** * This class implements accessibility support for the * JMenuBar class. It provides an implementation of the * Java Accessibility API appropriate to menu bar user-interface * elements. *

* Warning: * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. */ @SuppressWarnings("serial") protected class AccessibleJMenuBar extends AccessibleJComponent implements AccessibleSelection { /** * Get the accessible state set of this object. * * @return an instance of AccessibleState containing the current state * of the object */ public AccessibleStateSet getAccessibleStateSet() { AccessibleStateSet states = super.getAccessibleStateSet(); return states; } /** * Get the role of this object. * * @return an instance of AccessibleRole describing the role of the * object */ public AccessibleRole getAccessibleRole() { return AccessibleRole.MENU_BAR; } /** * Get the AccessibleSelection associated with this object. In the * implementation of the Java Accessibility API for this class, * return this object, which is responsible for implementing the * AccessibleSelection interface on behalf of itself. * * @return this object */ public AccessibleSelection getAccessibleSelection() { return this; } /** * Returns 1 if a menu is currently selected in this menu bar. * * @return 1 if a menu is currently selected, else 0 */ public int getAccessibleSelectionCount() { if (isSelected()) { return 1; } else { return 0; } } /** * Returns the currently selected menu if one is selected, * otherwise null. */ public Accessible getAccessibleSelection(int i) { if (isSelected()) { if (i != 0) { // single selection model for JMenuBar return null; } int j = getSelectionModel().getSelectedIndex(); if (getComponentAtIndex(j) instanceof Accessible) { return (Accessible) getComponentAtIndex(j); } } return null; } /** * Returns true if the current child of this object is selected. * * @param i the zero-based index of the child in this Accessible * object. * @see AccessibleContext#getAccessibleChild */ public boolean isAccessibleChildSelected(int i) { return (i == getSelectionModel().getSelectedIndex()); } /** * Selects the nth menu in the menu bar, forcing it to * pop up. If another menu is popped up, this will force * it to close. If the nth menu is already selected, this * method has no effect. * * @param i the zero-based index of selectable items * @see #getAccessibleStateSet */ public void addAccessibleSelection(int i) { // first close up any open menu int j = getSelectionModel().getSelectedIndex(); if (i == j) { return; } if (j >= 0 && j < getMenuCount()) { JMenu menu = getMenu(j); if (menu != null) { MenuSelectionManager.defaultManager().setSelectedPath(null); // menu.setPopupMenuVisible(false); } } // now popup the new menu getSelectionModel().setSelectedIndex(i); JMenu menu = getMenu(i); if (menu != null) { MenuElement[] me = new MenuElement[3]; me[0] = JMenuBar.this; me[1] = menu; me[2] = menu.getPopupMenu(); MenuSelectionManager.defaultManager().setSelectedPath(me); // menu.setPopupMenuVisible(true); } } /** * Removes the nth selected item in the object from the object's * selection. If the nth item isn't currently selected, this * method has no effect. Otherwise, it closes the popup menu. * * @param i the zero-based index of selectable items */ public void removeAccessibleSelection(int i) { if (i >= 0 && i < getMenuCount()) { JMenu menu = getMenu(i); if (menu != null) { MenuSelectionManager.defaultManager().setSelectedPath(null); // menu.setPopupMenuVisible(false); } getSelectionModel().setSelectedIndex(-1); } } /** * Clears the selection in the object, so that nothing in the * object is selected. This will close any open menu. */ public void clearAccessibleSelection() { int i = getSelectionModel().getSelectedIndex(); if (i >= 0 && i < getMenuCount()) { JMenu menu = getMenu(i); if (menu != null) { MenuSelectionManager.defaultManager().setSelectedPath(null); // menu.setPopupMenuVisible(false); } } getSelectionModel().setSelectedIndex(-1); } /** * Normally causes every selected item in the object to be selected * if the object supports multiple selections. This method * makes no sense in a menu bar, and so does nothing. */ public void selectAllAccessibleSelection() { } } // internal class AccessibleJMenuBar /** * Subclassed to check all the child menus. * @since 1.3 */ protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { // See if we have a local binding. boolean retValue = super.processKeyBinding(ks, e, condition, pressed); if (!retValue) { MenuElement[] subElements = getSubElements(); for (MenuElement subElement : subElements) { if (processBindingForKeyStrokeRecursive( subElement, ks, e, condition, pressed)) { return true; } } } return retValue; } static boolean processBindingForKeyStrokeRecursive(MenuElement elem, KeyStroke ks, KeyEvent e, int condition, boolean pressed) { if (elem == null) { return false; } Component c = elem.getComponent(); if ( !(c.isVisible() || (c instanceof JPopupMenu)) || !c.isEnabled() ) { return false; } if (c != null && c instanceof JComponent && ((JComponent)c).processKeyBinding(ks, e, condition, pressed)) { return true; } MenuElement[] subElements = elem.getSubElements(); for (MenuElement subElement : subElements) { if (processBindingForKeyStrokeRecursive(subElement, ks, e, condition, pressed)) { return true; // We don't, pass along to children JMenu's } } return false; } /** * Overrides JComponent.addNotify to register this * menu bar with the current keyboard manager. */ public void addNotify() { super.addNotify(); KeyboardManager.getCurrentManager().registerMenuBar(this); } /** * Overrides JComponent.removeNotify to unregister this * menu bar with the current keyboard manager. */ public void removeNotify() { super.removeNotify(); KeyboardManager.getCurrentManager().unregisterMenuBar(this); } private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); if (getUIClassID().equals(uiClassID)) { byte count = JComponent.getWriteObjCounter(this); JComponent.setWriteObjCounter(this, --count); if (count == 0 && ui != null) { ui.installUI(this); } } Object[] kvData = new Object[4]; int n = 0; if (selectionModel instanceof Serializable) { kvData[n++] = "selectionModel"; kvData[n++] = selectionModel; } s.writeObject(kvData); } /** * See JComponent.readObject() for information about serialization * in Swing. */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); Object[] kvData = (Object[])(s.readObject()); for(int i = 0; i < kvData.length; i += 2) { if (kvData[i] == null) { break; } else if (kvData[i].equals("selectionModel")) { selectionModel = (SingleSelectionModel)kvData[i + 1]; } } } }