| /* | |
| * Copyright (c) 1997, 2023, 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.ComponentOrientation; | |
| import java.awt.Container; | |
| import java.awt.Dimension; | |
| import java.awt.GraphicsConfiguration; | |
| import java.awt.GraphicsDevice; | |
| import java.awt.GraphicsEnvironment; | |
| import java.awt.Insets; | |
| import java.awt.Point; | |
| import java.awt.Rectangle; | |
| import java.awt.Toolkit; | |
| import java.awt.event.KeyEvent; | |
| import java.awt.event.WindowAdapter; | |
| import java.awt.event.WindowEvent; | |
| import java.beans.BeanProperty; | |
| import java.beans.JavaBean; | |
| import java.beans.PropertyChangeListener; | |
| import java.io.IOException; | |
| import java.io.ObjectOutputStream; | |
| import java.io.Serial; | |
| import java.io.Serializable; | |
| import java.util.ArrayList; | |
| import javax.accessibility.Accessible; | |
| import javax.accessibility.AccessibleContext; | |
| import javax.accessibility.AccessibleRole; | |
| import javax.accessibility.AccessibleSelection; | |
| import javax.swing.event.ChangeEvent; | |
| import javax.swing.event.ChangeListener; | |
| import javax.swing.event.EventListenerList; | |
| import javax.swing.event.MenuEvent; | |
| import javax.swing.event.MenuListener; | |
| import javax.swing.plaf.MenuItemUI; | |
| import javax.swing.plaf.PopupMenuUI; | |
| /** | |
| * An implementation of a menu -- a popup window containing | |
| * <code>JMenuItem</code>s that | |
| * is displayed when the user selects an item on the <code>JMenuBar</code>. | |
| * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can | |
| * also contain <code>JSeparator</code>s. | |
| * <p> | |
| * In essence, a menu is a button with an associated <code>JPopupMenu</code>. | |
| * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the | |
| * "button" is on the <code>JMenuBar</code>, the menu is a top-level window. | |
| * If the "button" is another menu item, then the <code>JPopupMenu</code> is | |
| * "pull-right" menu. | |
| * <p> | |
| * Menus can be configured, and to some degree controlled, by | |
| * <code><a href="Action.html">Action</a></code>s. Using an | |
| * <code>Action</code> with a menu has many benefits beyond directly | |
| * configuring a menu. Refer to <a href="Action.html#buttonActions"> | |
| * Swing Components Supporting <code>Action</code></a> for more | |
| * details, and you can find more information in <a | |
| * href="https://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How | |
| * to Use Actions</a>, a section in <em>The Java Tutorial</em>. | |
| * <p> | |
| * For information and examples of using menus see | |
| * <a href="https://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>, | |
| * a section in <em>The Java Tutorial.</em> | |
| * <p> | |
| * <strong>Warning:</strong> Swing is not thread safe. For more | |
| * information see <a | |
| * href="package-summary.html#threading">Swing's Threading | |
| * Policy</a>. | |
| * <p> | |
| * <strong>Warning:</strong> | |
| * 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 <code>java.beans</code> package. | |
| * Please see {@link java.beans.XMLEncoder}. | |
| * | |
| * @author Georges Saab | |
| * @author David Karlton | |
| * @author Arnaud Weber | |
| * @see JMenuItem | |
| * @see JSeparator | |
| * @see JMenuBar | |
| * @see JPopupMenu | |
| * @since 1.2 | |
| */ | |
| @JavaBean(description = "A popup window containing menu items displayed in a menu bar.") | |
| @SwingContainer | |
| @SuppressWarnings("serial") | |
| public class JMenu extends JMenuItem implements Accessible,MenuElement | |
| { | |
| /** | |
| * @see #getUIClassID | |
| * @see #readObject | |
| */ | |
| private static final String uiClassID = "MenuUI"; | |
| /* | |
| * The popup menu portion of the menu. | |
| */ | |
| private JPopupMenu popupMenu; | |
| /* | |
| * The button's model listeners. Default is <code>null</code>. | |
| */ | |
| private ChangeListener menuChangeListener = null; | |
| /* | |
| * Only one <code>MenuEvent</code> is needed for each menu since the | |
| * event's only state is the source property. The source of events | |
| * generated is always "this". Default is <code>null</code>. | |
| */ | |
| private MenuEvent menuEvent = null; | |
| /* | |
| * Used by the look and feel (L&F) code to handle | |
| * implementation specific menu behaviors. | |
| */ | |
| private int delay; | |
| /* | |
| * Location of the popup component. Location is <code>null</code> | |
| * if it was not customized by <code>setMenuLocation</code> | |
| */ | |
| private Point customMenuLocation = 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. | |
| /** | |
| * Constructs a new <code>JMenu</code> with no text. | |
| */ | |
| public JMenu() { | |
| this(""); | |
| } | |
| /** | |
| * Constructs a new <code>JMenu</code> with the supplied string | |
| * as its text. | |
| * | |
| * @param s the text for the menu label | |
| */ | |
| public JMenu(String s) { | |
| super(s); | |
| } | |
| /** | |
| * Constructs a menu whose properties are taken from the | |
| * <code>Action</code> supplied. | |
| * @param a an <code>Action</code> | |
| * | |
| * @since 1.3 | |
| */ | |
| public JMenu(Action a) { | |
| this(); | |
| setAction(a); | |
| } | |
| /** | |
| * Constructs a new <code>JMenu</code> with the supplied string as | |
| * its text and specified as a tear-off menu or not. | |
| * | |
| * @param s the text for the menu label | |
| * @param b can the menu be torn off (not yet implemented) | |
| */ | |
| public JMenu(String s, boolean b) { | |
| this(s); | |
| } | |
| /** | |
| * Overridden to do nothing. We want JMenu to be focusable, but | |
| * <code>JMenuItem</code> doesn't want to be, thus we override this | |
| * to do nothing. We don't invoke <code>setFocusable(true)</code> after | |
| * super's constructor has completed as this has the side effect that | |
| * <code>JMenu</code> will be considered traversable via the | |
| * keyboard, which we don't want. Making a Component traversable by | |
| * the keyboard after invoking <code>setFocusable(true)</code> is OK, | |
| * as <code>setFocusable</code> is new API | |
| * and is speced as such, but internally we don't want to use it like | |
| * this else we change the keyboard traversability. | |
| */ | |
| void initFocusability() { | |
| } | |
| /** | |
| * Resets the UI property with a value from the current look and feel. | |
| * | |
| * @see JComponent#updateUI | |
| */ | |
| public void updateUI() { | |
| setUI((MenuItemUI)UIManager.getUI(this)); | |
| if ( popupMenu != null ) | |
| { | |
| popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu)); | |
| } | |
| } | |
| /** | |
| * Returns the name of the L&F class that renders this component. | |
| * | |
| * @return the string "MenuUI" | |
| * @see JComponent#getUIClassID | |
| * @see UIDefaults#getUI | |
| */ | |
| @BeanProperty(bound = false) | |
| public String getUIClassID() { | |
| return uiClassID; | |
| } | |
| // public void repaint(long tm, int x, int y, int width, int height) { | |
| // Thread.currentThread().dumpStack(); | |
| // super.repaint(tm,x,y,width,height); | |
| // } | |
| /** | |
| * Sets the data model for the "menu button" -- the label | |
| * that the user clicks to open or close the menu. | |
| * | |
| * @param newModel the <code>ButtonModel</code> | |
| * @see #getModel | |
| */ | |
| public void setModel(ButtonModel newModel) { | |
| ButtonModel oldModel = getModel(); | |
| super.setModel(newModel); | |
| if (oldModel != null && menuChangeListener != null) { | |
| oldModel.removeChangeListener(menuChangeListener); | |
| menuChangeListener = null; | |
| } | |
| model = newModel; | |
| if (newModel != null) { | |
| menuChangeListener = createMenuChangeListener(); | |
| newModel.addChangeListener(menuChangeListener); | |
| } | |
| } | |
| /** | |
| * Returns true if the menu is currently selected (highlighted). | |
| * | |
| * @return true if the menu is selected, else false | |
| */ | |
| public boolean isSelected() { | |
| return getModel().isSelected(); | |
| } | |
| /** | |
| * Sets the selection status of the menu. | |
| * | |
| * @param b true to select (highlight) the menu; false to de-select | |
| * the menu | |
| */ | |
| @BeanProperty(expert = true, hidden = true, description | |
| = "When the menu is selected, its popup child is shown.") | |
| public void setSelected(boolean b) { | |
| ButtonModel model = getModel(); | |
| boolean oldValue = model.isSelected(); | |
| // TIGER - 4840653 | |
| // Removed code which fired an AccessibleState.SELECTED | |
| // PropertyChangeEvent since this resulted in two | |
| // identical events being fired since | |
| // AbstractButton.fireItemStateChanged also fires the | |
| // same event. This caused screen readers to speak the | |
| // name of the item twice. | |
| if (b != model.isSelected()) { | |
| getModel().setSelected(b); | |
| } | |
| } | |
| /** | |
| * Returns true if the menu's popup window is visible. | |
| * | |
| * @return true if the menu is visible, else false | |
| */ | |
| public boolean isPopupMenuVisible() { | |
| ensurePopupMenuCreated(); | |
| return popupMenu.isVisible(); | |
| } | |
| /** | |
| * Sets the visibility of the menu's popup. If the menu is | |
| * not enabled, this method will have no effect. | |
| * | |
| * @param b a boolean value -- true to make the menu visible, | |
| * false to hide it | |
| */ | |
| @BeanProperty(bound = false, expert = true, hidden = true, description | |
| = "The popup menu's visibility") | |
| public void setPopupMenuVisible(boolean b) { | |
| if (DEBUG) { | |
| System.out.println("in JMenu.setPopupMenuVisible " + b); | |
| // Thread.dumpStack(); | |
| } | |
| boolean isVisible = isPopupMenuVisible(); | |
| if (b != isVisible && (isEnabled() || !b)) { | |
| ensurePopupMenuCreated(); | |
| if ((b==true) && isShowing()) { | |
| // Set location of popupMenu (pulldown or pullright) | |
| Point p = getCustomMenuLocation(); | |
| if (p == null) { | |
| p = getPopupMenuOrigin(); | |
| } | |
| getPopupMenu().show(this, p.x, p.y); | |
| } else { | |
| getPopupMenu().setVisible(false); | |
| } | |
| } | |
| } | |
| /** | |
| * Computes the origin for the <code>JMenu</code>'s popup menu. | |
| * This method uses Look and Feel properties named | |
| * <code>Menu.menuPopupOffsetX</code>, | |
| * <code>Menu.menuPopupOffsetY</code>, | |
| * <code>Menu.submenuPopupOffsetX</code>, and | |
| * <code>Menu.submenuPopupOffsetY</code> | |
| * to adjust the exact location of popup. | |
| * | |
| * @return a <code>Point</code> in the coordinate space of the | |
| * menu which should be used as the origin | |
| * of the <code>JMenu</code>'s popup menu | |
| * | |
| * @since 1.3 | |
| */ | |
| protected Point getPopupMenuOrigin() { | |
| int x; | |
| int y; | |
| JPopupMenu pm = getPopupMenu(); | |
| // Figure out the sizes needed to calculate the menu position | |
| Dimension s = getSize(); | |
| Dimension pmSize = pm.getSize(); | |
| // For the first time the menu is popped up, | |
| // the size has not yet been initiated | |
| if (pmSize.width==0) { | |
| pmSize = pm.getPreferredSize(); | |
| } | |
| Point position = getLocationOnScreen(); | |
| Toolkit toolkit = Toolkit.getDefaultToolkit(); | |
| GraphicsConfiguration gc = getGraphicsConfiguration(); | |
| Rectangle screenBounds = new Rectangle(toolkit.getScreenSize()); | |
| GraphicsEnvironment ge = | |
| GraphicsEnvironment.getLocalGraphicsEnvironment(); | |
| GraphicsDevice[] gd = ge.getScreenDevices(); | |
| for(int i = 0; i < gd.length; i++) { | |
| if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) { | |
| GraphicsConfiguration dgc = | |
| gd[i].getDefaultConfiguration(); | |
| if(dgc.getBounds().contains(position)) { | |
| gc = dgc; | |
| break; | |
| } | |
| } | |
| } | |
| if (gc != null) { | |
| screenBounds = gc.getBounds(); | |
| // take screen insets (e.g. taskbar) into account | |
| Insets screenInsets = toolkit.getScreenInsets(gc); | |
| screenBounds.width -= | |
| Math.abs(screenInsets.left + screenInsets.right); | |
| screenBounds.height -= | |
| Math.abs(screenInsets.top + screenInsets.bottom); | |
| position.x -= Math.abs(screenInsets.left); | |
| position.y -= Math.abs(screenInsets.top); | |
| } | |
| Container parent = getParent(); | |
| if (parent instanceof JPopupMenu) { | |
| // We are a submenu (pull-right) | |
| int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX"); | |
| int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY"); | |
| if( SwingUtilities.isLeftToRight(this) ) { | |
| // First determine x: | |
| x = s.width + xOffset; // Prefer placement to the right | |
| if (position.x + x + pmSize.width >= screenBounds.width | |
| + screenBounds.x && | |
| // popup doesn't fit - place it wherever there's more room | |
| screenBounds.width - s.width < 2*(position.x | |
| - screenBounds.x)) { | |
| x = 0 - xOffset - pmSize.width; | |
| } | |
| } else { | |
| // First determine x: | |
| x = 0 - xOffset - pmSize.width; // Prefer placement to the left | |
| if (position.x + x < screenBounds.x && | |
| // popup doesn't fit - place it wherever there's more room | |
| screenBounds.width - s.width > 2*(position.x - | |
| screenBounds.x)) { | |
| x = s.width + xOffset; | |
| } | |
| } | |
| // Then the y: | |
| y = yOffset; // Prefer dropping down | |
| if (position.y + y + pmSize.height >= screenBounds.height | |
| + screenBounds.y && | |
| // popup doesn't fit - place it wherever there's more room | |
| screenBounds.height - s.height < 2*(position.y | |
| - screenBounds.y)) { | |
| y = s.height - yOffset - pmSize.height; | |
| } | |
| } else { | |
| // We are a toplevel menu (pull-down) | |
| int xOffset = UIManager.getInt("Menu.menuPopupOffsetX"); | |
| int yOffset = UIManager.getInt("Menu.menuPopupOffsetY"); | |
| if( SwingUtilities.isLeftToRight(this) ) { | |
| // First determine the x: | |
| x = xOffset; // Extend to the right | |
| if (position.x + x + pmSize.width >= screenBounds.width | |
| + screenBounds.x && | |
| // popup doesn't fit - place it wherever there's more room | |
| screenBounds.width - s.width < 2*(position.x | |
| - screenBounds.x)) { | |
| x = s.width - xOffset - pmSize.width; | |
| } | |
| } else { | |
| // First determine the x: | |
| x = s.width - xOffset - pmSize.width; // Extend to the left | |
| if (position.x + x < screenBounds.x && | |
| // popup doesn't fit - place it wherever there's more room | |
| screenBounds.width - s.width > 2*(position.x | |
| - screenBounds.x)) { | |
| x = xOffset; | |
| } | |
| } | |
| // Then the y: | |
| y = s.height + yOffset; // Prefer dropping down | |
| if (position.y + y + pmSize.height >= screenBounds.height | |
| + screenBounds.y && | |
| // popup doesn't fit - place it wherever there's more room | |
| screenBounds.height - s.height < 2*(position.y | |
| - screenBounds.y)) { | |
| y = 0 - yOffset - pmSize.height; // Otherwise drop 'up' | |
| } | |
| } | |
| // Note that the position may be later adjusted to fit the menu into the screen if possible. | |
| // However, the code that does it (JPopupMenu.adjustPopupLocationToFitScreen) has no idea which screen | |
| // to fit into, and determines it by the position calculated here, so we need to make sure it's on | |
| // the correct screen, otherwise the menu may appear on the wrong screen (JDK-6415065). | |
| // (Both x and y are relative to the JMenu position here, that's why we need these +-position.x/y here.) | |
| if (position.y + y < screenBounds.y) { // Above the current screen? | |
| y = screenBounds.y - position.y; // The top of the screen relative to this JMenu. | |
| } | |
| if (position.y + y >= screenBounds.y + screenBounds.height) { // Below the current screen? | |
| y = screenBounds.y + screenBounds.height - 1 - position.y; // The bottom of the screen... | |
| } | |
| if (position.x + x < screenBounds.x) { // To the left of the current screen? | |
| x = screenBounds.x - position.x; // The left edge of the screen... | |
| } | |
| if (position.x + x >= screenBounds.x + screenBounds.width) { // To the right of the current screen? | |
| x = screenBounds.x + screenBounds.width - 1 - position.x; // The right edge of the screen... | |
| } | |
| return new Point(x,y); | |
| } | |
| /** | |
| * Returns the suggested delay, in milliseconds, before submenus | |
| * are popped up or down. | |
| * Each look and feel (L&F) may determine its own policy for | |
| * observing the <code>delay</code> property. | |
| * In most cases, the delay is not observed for top level menus | |
| * or while dragging. The default for <code>delay</code> is 0. | |
| * This method is a property of the look and feel code and is used | |
| * to manage the idiosyncrasies of the various UI implementations. | |
| * | |
| * | |
| * @return the <code>delay</code> property | |
| */ | |
| public int getDelay() { | |
| return delay; | |
| } | |
| /** | |
| * Sets the suggested delay before the menu's <code>PopupMenu</code> | |
| * is popped up or down. Each look and feel (L&F) may determine | |
| * it's own policy for observing the delay property. In most cases, | |
| * the delay is not observed for top level menus or while dragging. | |
| * This method is a property of the look and feel code and is used | |
| * to manage the idiosyncrasies of the various UI implementations. | |
| * | |
| * @param d the number of milliseconds to delay | |
| * @throws IllegalArgumentException if <code>d</code> | |
| * is less than 0 | |
| */ | |
| @BeanProperty(bound = false, expert = true, description | |
| = "The delay between menu selection and making the popup menu visible") | |
| public void setDelay(int d) { | |
| if (d < 0) | |
| throw new IllegalArgumentException("Delay must be a positive integer"); | |
| delay = d; | |
| } | |
| /** | |
| * The window-closing listener for the popup. | |
| * | |
| * @see WinListener | |
| */ | |
| protected WinListener popupListener; | |
| private void ensurePopupMenuCreated() { | |
| if (popupMenu == null) { | |
| final JMenu thisMenu = this; | |
| this.popupMenu = new JPopupMenu(); | |
| popupMenu.setInvoker(this); | |
| popupListener = createWinListener(popupMenu); | |
| } | |
| } | |
| /* | |
| * Return the customized location of the popup component. | |
| */ | |
| private Point getCustomMenuLocation() { | |
| return customMenuLocation; | |
| } | |
| /** | |
| * Sets the location of the popup component. | |
| * | |
| * @param x the x coordinate of the popup's new position | |
| * @param y the y coordinate of the popup's new position | |
| */ | |
| public void setMenuLocation(int x, int y) { | |
| customMenuLocation = new Point(x, y); | |
| if (popupMenu != null) | |
| popupMenu.setLocation(x, y); | |
| } | |
| /** | |
| * Appends a menu item to the end of this menu. | |
| * Returns the menu item added. | |
| * | |
| * @param menuItem the <code>JMenuitem</code> to be added | |
| * @return the <code>JMenuItem</code> added | |
| */ | |
| public JMenuItem add(JMenuItem menuItem) { | |
| ensurePopupMenuCreated(); | |
| return popupMenu.add(menuItem); | |
| } | |
| /** | |
| * Appends a component to the end of this menu. | |
| * Returns the component added. | |
| * | |
| * @param c the <code>Component</code> to add | |
| * @return the <code>Component</code> added | |
| */ | |
| public Component add(Component c) { | |
| ensurePopupMenuCreated(); | |
| popupMenu.add(c); | |
| return c; | |
| } | |
| /** | |
| * Adds the specified component to this container at the given | |
| * position. If <code>index</code> equals -1, the component will | |
| * be appended to the end. | |
| * @param c the <code>Component</code> to add | |
| * @param index the position at which to insert the component | |
| * @return the <code>Component</code> added | |
| * @see #remove | |
| * @see java.awt.Container#add(Component, int) | |
| */ | |
| public Component add(Component c, int index) { | |
| ensurePopupMenuCreated(); | |
| popupMenu.add(c, index); | |
| return c; | |
| } | |
| /** | |
| * Creates a new menu item with the specified text and appends | |
| * it to the end of this menu. | |
| * | |
| * @param s the string for the menu item to be added | |
| * @return the new {@code JMenuItem} | |
| */ | |
| public JMenuItem add(String s) { | |
| return add(new JMenuItem(s)); | |
| } | |
| /** | |
| * Creates a new menu item attached to the specified {@code Action} object | |
| * and appends it to the end of this menu. | |
| * | |
| * @param a the {@code Action} for the menu item to be added | |
| * @return the new {@code JMenuItem} | |
| * @see Action | |
| */ | |
| public JMenuItem add(Action a) { | |
| JMenuItem mi = createActionComponent(a); | |
| mi.setAction(a); | |
| add(mi); | |
| return mi; | |
| } | |
| /** | |
| * Factory method which creates the <code>JMenuItem</code> for | |
| * <code>Action</code>s added to the <code>JMenu</code>. | |
| * | |
| * @param a the <code>Action</code> for the menu item to be added | |
| * @return the new menu item | |
| * @see Action | |
| * | |
| * @since 1.3 | |
| */ | |
| protected JMenuItem createActionComponent(Action a) { | |
| JMenuItem mi = new JMenuItem() { | |
| protected PropertyChangeListener createActionPropertyChangeListener(Action a) { | |
| PropertyChangeListener pcl = createActionChangeListener(this); | |
| if (pcl == null) { | |
| pcl = super.createActionPropertyChangeListener(a); | |
| } | |
| return pcl; | |
| } | |
| }; | |
| mi.setHorizontalTextPosition(JButton.TRAILING); | |
| mi.setVerticalTextPosition(JButton.CENTER); | |
| return mi; | |
| } | |
| /** | |
| * Returns a properly configured {@code PropertyChangeListener} | |
| * which updates the control as changes to the {@code Action} occur. | |
| * | |
| * @param b a menu item for which to create a {@code PropertyChangeListener} | |
| * @return a {@code PropertyChangeListener} for {@code b} | |
| */ | |
| protected PropertyChangeListener createActionChangeListener(JMenuItem b) { | |
| return b.createActionPropertyChangeListener0(b.getAction()); | |
| } | |
| /** | |
| * Appends a new separator to the end of the menu. | |
| */ | |
| public void addSeparator() | |
| { | |
| ensurePopupMenuCreated(); | |
| popupMenu.addSeparator(); | |
| } | |
| /** | |
| * Inserts a new menu item with the specified text at a | |
| * given position. | |
| * | |
| * @param s the text for the menu item to add | |
| * @param pos an integer specifying the position at which to add the | |
| * new menu item | |
| * @throws IllegalArgumentException when the value of | |
| * <code>pos</code> < 0 | |
| */ | |
| public void insert(String s, int pos) { | |
| if (pos < 0) { | |
| throw new IllegalArgumentException("index less than zero."); | |
| } | |
| ensurePopupMenuCreated(); | |
| popupMenu.insert(new JMenuItem(s), pos); | |
| } | |
| /** | |
| * Inserts the specified <code>JMenuitem</code> at a given position. | |
| * | |
| * @param mi the <code>JMenuitem</code> to add | |
| * @param pos an integer specifying the position at which to add the | |
| * new <code>JMenuitem</code> | |
| * @return the new menu item | |
| * @throws IllegalArgumentException if the value of | |
| * <code>pos</code> < 0 | |
| */ | |
| public JMenuItem insert(JMenuItem mi, int pos) { | |
| if (pos < 0) { | |
| throw new IllegalArgumentException("index less than zero."); | |
| } | |
| ensurePopupMenuCreated(); | |
| popupMenu.insert(mi, pos); | |
| return mi; | |
| } | |
| /** | |
| * Inserts a new menu item attached to the specified <code>Action</code> | |
| * object at a given position. | |
| * | |
| * @param a the <code>Action</code> object for the menu item to add | |
| * @param pos an integer specifying the position at which to add the | |
| * new menu item | |
| * @return the new menu item | |
| * @throws IllegalArgumentException if the value of | |
| * <code>pos</code> < 0 | |
| */ | |
| public JMenuItem insert(Action a, int pos) { | |
| if (pos < 0) { | |
| throw new IllegalArgumentException("index less than zero."); | |
| } | |
| ensurePopupMenuCreated(); | |
| JMenuItem mi = new JMenuItem(a); | |
| mi.setHorizontalTextPosition(JButton.TRAILING); | |
| mi.setVerticalTextPosition(JButton.CENTER); | |
| popupMenu.insert(mi, pos); | |
| return mi; | |
| } | |
| /** | |
| * Inserts a separator at the specified position. | |
| * | |
| * @param index an integer specifying the position at which to | |
| * insert the menu separator | |
| * @throws IllegalArgumentException if the value of | |
| * <code>index</code> < 0 | |
| */ | |
| public void insertSeparator(int index) { | |
| if (index < 0) { | |
| throw new IllegalArgumentException("index less than zero."); | |
| } | |
| ensurePopupMenuCreated(); | |
| popupMenu.insert( new JPopupMenu.Separator(), index ); | |
| } | |
| /** | |
| * Returns the {@code JMenuItem} at the specified position. | |
| * If the component at {@code pos} is not a menu item, | |
| * {@code null} is returned. | |
| * This method is included for AWT compatibility. | |
| * | |
| * @param pos an integer specifying the position | |
| * @return the menu item at the specified position; or <code>null</code> | |
| * if the item as the specified position is not a menu item | |
| * @throws IllegalArgumentException if the value of | |
| * {@code pos} < 0 | |
| */ | |
| public JMenuItem getItem(int pos) { | |
| if (pos < 0) { | |
| throw new IllegalArgumentException("index less than zero."); | |
| } | |
| Component c = getMenuComponent(pos); | |
| if (c instanceof JMenuItem) { | |
| JMenuItem mi = (JMenuItem) c; | |
| return mi; | |
| } | |
| // 4173633 | |
| return null; | |
| } | |
| /** | |
| * Returns the number of items on the menu, including separators. | |
| * This method is included for AWT compatibility. | |
| * | |
| * @return an integer equal to the number of items on the menu | |
| * @see #getMenuComponentCount | |
| */ | |
| @BeanProperty(bound = false) | |
| public int getItemCount() { | |
| return getMenuComponentCount(); | |
| } | |
| /** | |
| * Returns true if the menu can be torn off. This method is not | |
| * yet implemented. | |
| * | |
| * @return true if the menu can be torn off, else false | |
| * @throws Error if invoked -- this method is not yet implemented | |
| */ | |
| @BeanProperty(bound = false) | |
| public boolean isTearOff() { | |
| throw new Error("boolean isTearOff() {} not yet implemented"); | |
| } | |
| /** | |
| * Removes the specified menu item from this menu. If there is no | |
| * popup menu, this method will have no effect. | |
| * | |
| * @param item the <code>JMenuItem</code> to be removed from the menu | |
| */ | |
| public void remove(JMenuItem item) { | |
| if (popupMenu != null) | |
| popupMenu.remove(item); | |
| } | |
| /** | |
| * Removes the menu item at the specified index from this menu. | |
| * | |
| * @param pos the position of the item to be removed | |
| * @throws IllegalArgumentException if the value of | |
| * <code>pos</code> < 0, or if <code>pos</code> | |
| * is greater than the number of menu items | |
| */ | |
| public void remove(int pos) { | |
| if (pos < 0) { | |
| throw new IllegalArgumentException("index less than zero."); | |
| } | |
| if (pos > getItemCount()) { | |
| throw new IllegalArgumentException("index greater than the number of items."); | |
| } | |
| if (popupMenu != null) | |
| popupMenu.remove(pos); | |
| } | |
| /** | |
| * Removes the component <code>c</code> from this menu. | |
| * | |
| * @param c the component to be removed | |
| */ | |
| public void remove(Component c) { | |
| if (popupMenu != null) | |
| popupMenu.remove(c); | |
| } | |
| /** | |
| * Removes all menu items from this menu. | |
| */ | |
| public void removeAll() { | |
| if (popupMenu != null) | |
| popupMenu.removeAll(); | |
| } | |
| /** | |
| * Returns the number of components on the menu. | |
| * | |
| * @return an integer containing the number of components on the menu | |
| */ | |
| @BeanProperty(bound = false) | |
| public int getMenuComponentCount() { | |
| int componentCount = 0; | |
| if (popupMenu != null) | |
| componentCount = popupMenu.getComponentCount(); | |
| return componentCount; | |
| } | |
| /** | |
| * Returns the component at position <code>n</code>. | |
| * | |
| * @param n the position of the component to be returned | |
| * @return the component requested, or <code>null</code> | |
| * if there is no popup menu | |
| * | |
| */ | |
| public Component getMenuComponent(int n) { | |
| if (popupMenu != null) | |
| return popupMenu.getComponent(n); | |
| return null; | |
| } | |
| /** | |
| * Returns an array of <code>Component</code>s of the menu's | |
| * subcomponents. Note that this returns all <code>Component</code>s | |
| * in the popup menu, including separators. | |
| * | |
| * @return an array of <code>Component</code>s or an empty array | |
| * if there is no popup menu | |
| */ | |
| @BeanProperty(bound = false) | |
| public Component[] getMenuComponents() { | |
| if (popupMenu != null) | |
| return popupMenu.getComponents(); | |
| return new Component[0]; | |
| } | |
| /** | |
| * Returns true if the menu is a 'top-level menu', that is, if it is | |
| * the direct child of a menubar. | |
| * | |
| * @return true if the menu is activated from the menu bar; | |
| * false if the menu is activated from a menu item | |
| * on another menu | |
| */ | |
| @BeanProperty(bound = false) | |
| public boolean isTopLevelMenu() { | |
| return getParent() instanceof JMenuBar; | |
| } | |
| /** | |
| * Returns true if the specified component exists in the | |
| * submenu hierarchy. | |
| * | |
| * @param c the <code>Component</code> to be tested | |
| * @return true if the <code>Component</code> exists, false otherwise | |
| */ | |
| public boolean isMenuComponent(Component c) { | |
| // Are we in the MenuItem part of the menu | |
| if (c == this) | |
| return true; | |
| // Are we in the PopupMenu? | |
| if (c instanceof JPopupMenu) { | |
| JPopupMenu comp = (JPopupMenu) c; | |
| if (comp == this.getPopupMenu()) | |
| return true; | |
| } | |
| // Are we in a Component on the PopupMenu | |
| int ncomponents = this.getMenuComponentCount(); | |
| Component[] component = this.getMenuComponents(); | |
| for (int i = 0 ; i < ncomponents ; i++) { | |
| Component comp = component[i]; | |
| // Are we in the current component? | |
| if (comp == c) | |
| return true; | |
| // Hmmm, what about Non-menu containers? | |
| // Recursive call for the Menu case | |
| if (comp instanceof JMenu) { | |
| JMenu subMenu = (JMenu) comp; | |
| if (subMenu.isMenuComponent(c)) | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| /* | |
| * Returns a point in the coordinate space of this menu's popupmenu | |
| * which corresponds to the point <code>p</code> in the menu's | |
| * coordinate space. | |
| * | |
| * @param p the point to be translated | |
| * @return the point in the coordinate space of this menu's popupmenu | |
| */ | |
| private Point translateToPopupMenu(Point p) { | |
| return translateToPopupMenu(p.x, p.y); | |
| } | |
| /* | |
| * Returns a point in the coordinate space of this menu's popupmenu | |
| * which corresponds to the point (x,y) in the menu's coordinate space. | |
| * | |
| * @param x the x coordinate of the point to be translated | |
| * @param y the y coordinate of the point to be translated | |
| * @return the point in the coordinate space of this menu's popupmenu | |
| */ | |
| private Point translateToPopupMenu(int x, int y) { | |
| int newX; | |
| int newY; | |
| if (getParent() instanceof JPopupMenu) { | |
| newX = x - getSize().width; | |
| newY = y; | |
| } else { | |
| newX = x; | |
| newY = y - getSize().height; | |
| } | |
| return new Point(newX, newY); | |
| } | |
| /** | |
| * Returns the popupmenu associated with this menu. If there is | |
| * no popupmenu, it will create one. | |
| * | |
| * @return the {@code JPopupMenu} associated with this menu | |
| */ | |
| @BeanProperty(bound = false) | |
| public JPopupMenu getPopupMenu() { | |
| ensurePopupMenuCreated(); | |
| return popupMenu; | |
| } | |
| /** | |
| * Adds a listener for menu events. | |
| * | |
| * @param l the listener to be added | |
| */ | |
| public void addMenuListener(MenuListener l) { | |
| listenerList.add(MenuListener.class, l); | |
| } | |
| /** | |
| * Removes a listener for menu events. | |
| * | |
| * @param l the listener to be removed | |
| */ | |
| public void removeMenuListener(MenuListener l) { | |
| listenerList.remove(MenuListener.class, l); | |
| } | |
| /** | |
| * Returns an array of all the <code>MenuListener</code>s added | |
| * to this JMenu with addMenuListener(). | |
| * | |
| * @return all of the <code>MenuListener</code>s added or an empty | |
| * array if no listeners have been added | |
| * @since 1.4 | |
| */ | |
| @BeanProperty(bound = false) | |
| public MenuListener[] getMenuListeners() { | |
| return listenerList.getListeners(MenuListener.class); | |
| } | |
| /** | |
| * Notifies all listeners that have registered interest for | |
| * notification on this event type. The event instance | |
| * is created lazily. | |
| * | |
| * @throws Error if there is a <code>null</code> listener | |
| * @see EventListenerList | |
| */ | |
| protected void fireMenuSelected() { | |
| if (DEBUG) { | |
| System.out.println("In JMenu.fireMenuSelected"); | |
| } | |
| // Guaranteed to return a non-null array | |
| Object[] listeners = listenerList.getListenerList(); | |
| // Process the listeners last to first, notifying | |
| // those that are interested in this event | |
| for (int i = listeners.length-2; i>=0; i-=2) { | |
| if (listeners[i]==MenuListener.class) { | |
| if (listeners[i+1]== null) { | |
| throw new Error(getText() +" has a NULL Listener!! " + i); | |
| } else { | |
| // Lazily create the event: | |
| if (menuEvent == null) | |
| menuEvent = new MenuEvent(this); | |
| ((MenuListener)listeners[i+1]).menuSelected(menuEvent); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Notifies all listeners that have registered interest for | |
| * notification on this event type. The event instance | |
| * is created lazily. | |
| * | |
| * @throws Error if there is a <code>null</code> listener | |
| * @see EventListenerList | |
| */ | |
| protected void fireMenuDeselected() { | |
| if (DEBUG) { | |
| System.out.println("In JMenu.fireMenuDeselected"); | |
| } | |
| // Guaranteed to return a non-null array | |
| Object[] listeners = listenerList.getListenerList(); | |
| // Process the listeners last to first, notifying | |
| // those that are interested in this event | |
| for (int i = listeners.length-2; i>=0; i-=2) { | |
| if (listeners[i]==MenuListener.class) { | |
| if (listeners[i+1]== null) { | |
| throw new Error(getText() +" has a NULL Listener!! " + i); | |
| } else { | |
| // Lazily create the event: | |
| if (menuEvent == null) | |
| menuEvent = new MenuEvent(this); | |
| ((MenuListener)listeners[i+1]).menuDeselected(menuEvent); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Notifies all listeners that have registered interest for | |
| * notification on this event type. The event instance | |
| * is created lazily. | |
| * | |
| * @throws Error if there is a <code>null</code> listener | |
| * @see EventListenerList | |
| */ | |
| protected void fireMenuCanceled() { | |
| if (DEBUG) { | |
| System.out.println("In JMenu.fireMenuCanceled"); | |
| } | |
| // Guaranteed to return a non-null array | |
| Object[] listeners = listenerList.getListenerList(); | |
| // Process the listeners last to first, notifying | |
| // those that are interested in this event | |
| for (int i = listeners.length-2; i>=0; i-=2) { | |
| if (listeners[i]==MenuListener.class) { | |
| if (listeners[i+1]== null) { | |
| throw new Error(getText() +" has a NULL Listener!! " | |
| + i); | |
| } else { | |
| // Lazily create the event: | |
| if (menuEvent == null) | |
| menuEvent = new MenuEvent(this); | |
| ((MenuListener)listeners[i+1]).menuCanceled(menuEvent); | |
| } | |
| } | |
| } | |
| } | |
| // Overridden to do nothing, JMenu doesn't support an accelerator | |
| void configureAcceleratorFromAction(Action a) { | |
| } | |
| @SuppressWarnings("serial") | |
| class MenuChangeListener implements ChangeListener, Serializable { | |
| boolean isSelected = false; | |
| public void stateChanged(ChangeEvent e) { | |
| ButtonModel model = (ButtonModel) e.getSource(); | |
| boolean modelSelected = model.isSelected(); | |
| if (modelSelected != isSelected) { | |
| if (modelSelected == true) { | |
| fireMenuSelected(); | |
| } else { | |
| fireMenuDeselected(); | |
| } | |
| isSelected = modelSelected; | |
| } | |
| } | |
| } | |
| private ChangeListener createMenuChangeListener() { | |
| return new MenuChangeListener(); | |
| } | |
| /** | |
| * Creates a window-closing listener for the popup. | |
| * | |
| * @param p the <code>JPopupMenu</code> | |
| * @return the new window-closing listener | |
| * | |
| * @see WinListener | |
| */ | |
| protected WinListener createWinListener(JPopupMenu p) { | |
| return new WinListener(p); | |
| } | |
| /** | |
| * A listener class that watches for a popup window closing. | |
| * When the popup is closing, the listener deselects the menu. | |
| * <p> | |
| * <strong>Warning:</strong> | |
| * 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 <code>java.beans</code> package. | |
| * Please see {@link java.beans.XMLEncoder}. | |
| */ | |
| @SuppressWarnings("serial") | |
| protected class WinListener extends WindowAdapter implements Serializable { | |
| JPopupMenu popupMenu; | |
| /** | |
| * Create the window listener for the specified popup. | |
| * | |
| * @param p the popup menu for which to create a listener | |
| * @since 1.4 | |
| */ | |
| public WinListener(JPopupMenu p) { | |
| this.popupMenu = p; | |
| } | |
| /** | |
| * Deselect the menu when the popup is closed from outside. | |
| */ | |
| public void windowClosing(WindowEvent e) { | |
| setSelected(false); | |
| } | |
| } | |
| /** | |
| * Messaged when the menubar selection changes to activate or | |
| * deactivate this menu. | |
| * Overrides <code>JMenuItem.menuSelectionChanged</code>. | |
| * | |
| * @param isIncluded true if this menu is active, false if | |
| * it is not | |
| */ | |
| public void menuSelectionChanged(boolean isIncluded) { | |
| if (DEBUG) { | |
| System.out.println("In JMenu.menuSelectionChanged to " + isIncluded); | |
| } | |
| setSelected(isIncluded); | |
| } | |
| /** | |
| * Returns an array of <code>MenuElement</code>s containing the submenu | |
| * for this menu component. If popup menu is <code>null</code> returns | |
| * an empty array. This method is required to conform to the | |
| * <code>MenuElement</code> interface. Note that since | |
| * <code>JSeparator</code>s do not conform to the <code>MenuElement</code> | |
| * interface, this array will only contain <code>JMenuItem</code>s. | |
| * | |
| * @return an array of <code>MenuElement</code> objects | |
| */ | |
| @BeanProperty(bound = false) | |
| public MenuElement[] getSubElements() { | |
| if(popupMenu == null) | |
| return new MenuElement[0]; | |
| else { | |
| MenuElement[] result = new MenuElement[1]; | |
| result[0] = popupMenu; | |
| return result; | |
| } | |
| } | |
| // implements javax.swing.MenuElement | |
| /** | |
| * Returns the <code>java.awt.Component</code> used to | |
| * paint this <code>MenuElement</code>. | |
| * The returned component is used to convert events and detect if | |
| * an event is inside a menu component. | |
| */ | |
| public Component getComponent() { | |
| return this; | |
| } | |
| /** | |
| * Sets the <code>ComponentOrientation</code> property of this menu | |
| * and all components contained within it. This includes all | |
| * components returned by {@link #getMenuComponents getMenuComponents}. | |
| * | |
| * @param o the new component orientation of this menu and | |
| * the components contained within it. | |
| * @throws NullPointerException if <code>orientation</code> is null. | |
| * @see java.awt.Component#setComponentOrientation | |
| * @see java.awt.Component#getComponentOrientation | |
| * @since 1.4 | |
| */ | |
| public void applyComponentOrientation(ComponentOrientation o) { | |
| super.applyComponentOrientation(o); | |
| if ( popupMenu != null ) { | |
| int ncomponents = getMenuComponentCount(); | |
| for (int i = 0 ; i < ncomponents ; ++i) { | |
| getMenuComponent(i).applyComponentOrientation(o); | |
| } | |
| popupMenu.setComponentOrientation(o); | |
| } | |
| } | |
| /** | |
| * Sets the orientation for this menu and its associated popup menu | |
| * determined by the {@code ComponentOrientation} argument. | |
| * | |
| * @param o the new orientation for this menu and | |
| * its associated popup menu. | |
| */ | |
| public void setComponentOrientation(ComponentOrientation o) { | |
| super.setComponentOrientation(o); | |
| if ( popupMenu != null ) { | |
| popupMenu.setComponentOrientation(o); | |
| } | |
| } | |
| /** | |
| * <code>setAccelerator</code> is not defined for <code>JMenu</code>. | |
| * Use <code>setMnemonic</code> instead. | |
| * @param keyStroke the keystroke combination which will invoke | |
| * the <code>JMenuItem</code>'s actionlisteners | |
| * without navigating the menu hierarchy | |
| * @throws Error if invoked -- this method is not defined for JMenu. | |
| * Use <code>setMnemonic</code> instead | |
| */ | |
| public void setAccelerator(KeyStroke keyStroke) { | |
| throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead."); | |
| } | |
| /** | |
| * Processes key stroke events such as mnemonics and accelerators. | |
| * | |
| * @param evt the key event to be processed | |
| */ | |
| protected void processKeyEvent(KeyEvent evt) { | |
| MenuSelectionManager.defaultManager().processKeyEvent(evt); | |
| if (evt.isConsumed()) | |
| return; | |
| super.processKeyEvent(evt); | |
| } | |
| /** | |
| * Programmatically performs a "click". This overrides the method | |
| * <code>AbstractButton.doClick</code> in order to make the menu pop up. | |
| * @param pressTime indicates the number of milliseconds the | |
| * button was pressed for | |
| */ | |
| public void doClick(int pressTime) { | |
| MenuElement[] me = buildMenuElementArray(this); | |
| MenuSelectionManager.defaultManager().setSelectedPath(me); | |
| } | |
| /* | |
| * Build an array of menu elements - from <code>PopupMenu</code> to | |
| * the root <code>JMenuBar</code>. | |
| * @param leaf the leaf node from which to start building up the array | |
| * @return the array of menu items | |
| */ | |
| private MenuElement[] buildMenuElementArray(JMenu leaf) { | |
| ArrayList<MenuElement> elements = new ArrayList<>(); | |
| Component current = leaf.getPopupMenu(); | |
| JPopupMenu pop; | |
| JMenu menu; | |
| JMenuBar bar; | |
| while (true) { | |
| if (current instanceof JPopupMenu) { | |
| pop = (JPopupMenu) current; | |
| elements.add(0, pop); | |
| current = pop.getInvoker(); | |
| } else if (current instanceof JMenu) { | |
| menu = (JMenu) current; | |
| elements.add(0, menu); | |
| current = menu.getParent(); | |
| } else if (current instanceof JMenuBar) { | |
| bar = (JMenuBar) current; | |
| elements.add(0, bar); | |
| break; | |
| } else { | |
| break; | |
| } | |
| } | |
| MenuElement[] me = elements.toArray(new MenuElement[0]); | |
| return me; | |
| } | |
| /** | |
| * See <code>readObject</code> and <code>writeObject</code> in | |
| * <code>JComponent</code> for more | |
| * information about serialization in Swing. | |
| */ | |
| @Serial | |
| 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); | |
| } | |
| } | |
| } | |
| /** | |
| * Returns a string representation of this <code>JMenu</code>. 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 <code>null</code>. | |
| * | |
| * @return a string representation of this JMenu. | |
| */ | |
| protected String paramString() { | |
| return super.paramString(); | |
| } | |
| ///////////////// | |
| // Accessibility support | |
| //////////////// | |
| /** | |
| * Gets the AccessibleContext associated with this JMenu. | |
| * For JMenus, the AccessibleContext takes the form of an | |
| * AccessibleJMenu. | |
| * A new AccessibleJMenu instance is created if necessary. | |
| * | |
| * @return an AccessibleJMenu that serves as the | |
| * AccessibleContext of this JMenu | |
| */ | |
| @BeanProperty(bound = false) | |
| public AccessibleContext getAccessibleContext() { | |
| if (accessibleContext == null) { | |
| accessibleContext = new AccessibleJMenu(); | |
| } | |
| return accessibleContext; | |
| } | |
| /** | |
| * This class implements accessibility support for the | |
| * <code>JMenu</code> class. It provides an implementation of the | |
| * Java Accessibility API appropriate to menu user-interface elements. | |
| * <p> | |
| * <strong>Warning:</strong> | |
| * 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 <code>java.beans</code> package. | |
| * Please see {@link java.beans.XMLEncoder}. | |
| */ | |
| @SuppressWarnings("serial") | |
| protected class AccessibleJMenu extends AccessibleJMenuItem | |
| implements AccessibleSelection { | |
| /** | |
| * Constructs an {@code AccessibleJMenu}. | |
| */ | |
| protected AccessibleJMenu() {} | |
| /** | |
| * Returns the number of accessible children in the object. If all | |
| * of the children of this object implement Accessible, than this | |
| * method should return the number of children of this object. | |
| * | |
| * @return the number of accessible children in the object. | |
| */ | |
| public int getAccessibleChildrenCount() { | |
| Component[] children = getMenuComponents(); | |
| int count = 0; | |
| for (Component child : children) { | |
| if (child instanceof Accessible) { | |
| count++; | |
| } | |
| } | |
| return count; | |
| } | |
| /** | |
| * Returns the nth Accessible child of the object. | |
| * | |
| * @param i zero-based index of child | |
| * @return the nth Accessible child of the object | |
| */ | |
| public Accessible getAccessibleChild(int i) { | |
| Component[] children = getMenuComponents(); | |
| int count = 0; | |
| for (Component child : children) { | |
| if (child instanceof Accessible) { | |
| if (count == i) { | |
| if (child instanceof JComponent) { | |
| // FIXME: [[[WDW - probably should set this when | |
| // the component is added to the menu. I tried | |
| // to do this in most cases, but the separators | |
| // added by addSeparator are hard to get to.]]] | |
| AccessibleContext ac = child.getAccessibleContext(); | |
| ac.setAccessibleParent(JMenu.this); | |
| } | |
| return (Accessible) child; | |
| } else { | |
| count++; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Get the role of this object. | |
| * | |
| * @return an instance of AccessibleRole describing the role of the | |
| * object | |
| * @see AccessibleRole | |
| */ | |
| public AccessibleRole getAccessibleRole() { | |
| return AccessibleRole.MENU; | |
| } | |
| /** | |
| * 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 sub-menu is currently selected in this menu. | |
| * | |
| * @return 1 if a menu is currently selected, else 0 | |
| */ | |
| public int getAccessibleSelectionCount() { | |
| MenuElement[] me = | |
| MenuSelectionManager.defaultManager().getSelectedPath(); | |
| if (me != null) { | |
| for (int i = 0; i < me.length; i++) { | |
| if (me[i] == JMenu.this) { // this menu is selected | |
| if (i+1 < me.length) { | |
| return 1; | |
| } | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Returns the currently selected sub-menu if one is selected, | |
| * otherwise null (there can only be one selection, and it can | |
| * only be a sub-menu, as otherwise menu items don't remain | |
| * selected). | |
| */ | |
| public Accessible getAccessibleSelection(int i) { | |
| // if i is a sub-menu & popped, return it | |
| if (i < 0 || i >= getItemCount()) { | |
| return null; | |
| } | |
| MenuElement[] me = | |
| MenuSelectionManager.defaultManager().getSelectedPath(); | |
| if (me != null) { | |
| for (int j = 0; j < me.length; j++) { | |
| if (me[j] == JMenu.this) { // this menu is selected | |
| // so find the next JMenuItem in the MenuElement | |
| // array, and return it! | |
| while (++j < me.length) { | |
| if (me[j] instanceof JMenuItem) { | |
| return (Accessible) me[j]; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Returns true if the current child of this object is selected | |
| * (that is, if this child is a popped-up submenu). | |
| * | |
| * @param i the zero-based index of the child in this Accessible | |
| * object. | |
| * @see AccessibleContext#getAccessibleChild | |
| */ | |
| public boolean isAccessibleChildSelected(int i) { | |
| // if i is a sub-menu and is pop-ed up, return true, else false | |
| MenuElement[] me = | |
| MenuSelectionManager.defaultManager().getSelectedPath(); | |
| if (me != null) { | |
| JMenuItem mi = JMenu.this.getItem(i); | |
| for (int j = 0; j < me.length; j++) { | |
| if (me[j] == mi) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Selects the <code>i</code>th menu in the menu. | |
| * If that item is a submenu, | |
| * it will pop up in response. If a different item is already | |
| * popped up, this will force it to close. If this is a sub-menu | |
| * that is already popped up (selected), this method has no | |
| * effect. | |
| * | |
| * @param i the index of the item to be selected | |
| * @see #getAccessibleStateSet | |
| */ | |
| public void addAccessibleSelection(int i) { | |
| if (i < 0 || i >= getItemCount()) { | |
| return; | |
| } | |
| JMenuItem mi = getItem(i); | |
| if (mi != null) { | |
| if (mi instanceof JMenu) { | |
| MenuElement[] me = buildMenuElementArray((JMenu) mi); | |
| MenuSelectionManager.defaultManager().setSelectedPath(me); | |
| } else { | |
| MenuSelectionManager.defaultManager().setSelectedPath(null); | |
| } | |
| } | |
| } | |
| /** | |
| * Removes the nth item from the selection. In general, menus | |
| * can only have one item within them selected at a time | |
| * (e.g. one sub-menu popped open). | |
| * | |
| * @param i the zero-based index of the selected item | |
| */ | |
| public void removeAccessibleSelection(int i) { | |
| if (i < 0 || i >= getItemCount()) { | |
| return; | |
| } | |
| JMenuItem mi = getItem(i); | |
| if (mi instanceof JMenu) { | |
| if (mi.isSelected()) { | |
| MenuElement[] old = | |
| MenuSelectionManager.defaultManager().getSelectedPath(); | |
| MenuElement[] me = new MenuElement[old.length-2]; | |
| for (int j = 0; j < old.length -2; j++) { | |
| me[j] = old[j]; | |
| } | |
| MenuSelectionManager.defaultManager().setSelectedPath(me); | |
| } | |
| } | |
| } | |
| /** | |
| * Clears the selection in the object, so that nothing in the | |
| * object is selected. This will close any open sub-menu. | |
| */ | |
| public void clearAccessibleSelection() { | |
| // if this menu is selected, reset selection to only go | |
| // to this menu; else do nothing | |
| MenuElement[] old = | |
| MenuSelectionManager.defaultManager().getSelectedPath(); | |
| if (old != null) { | |
| for (int j = 0; j < old.length; j++) { | |
| if (old[j] == JMenu.this) { // menu is in the selection! | |
| MenuElement[] me = new MenuElement[j+1]; | |
| System.arraycopy(old, 0, me, 0, j); | |
| me[j] = JMenu.this.getPopupMenu(); | |
| MenuSelectionManager.defaultManager().setSelectedPath(me); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * 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() { | |
| } | |
| } // inner class AccessibleJMenu | |
| } |