0001 /*
0002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing;
0027
0028 import java.awt.AWTEvent;
0029 import java.awt.Component;
0030 import java.awt.ComponentOrientation;
0031 import java.awt.Container;
0032 import java.awt.Dimension;
0033 import java.awt.Frame;
0034 import java.awt.Graphics;
0035 import java.awt.GraphicsConfiguration;
0036 import java.awt.GraphicsDevice;
0037 import java.awt.GraphicsEnvironment;
0038 import java.awt.Insets;
0039 import java.awt.Point;
0040 import java.awt.Polygon;
0041 import java.awt.Rectangle;
0042 import java.awt.Toolkit;
0043 import java.awt.event.*;
0044 import java.beans.*;
0045
0046 import java.util.*;
0047
0048 import java.io.Serializable;
0049 import java.io.ObjectOutputStream;
0050 import java.io.ObjectInputStream;
0051 import java.io.IOException;
0052
0053 import javax.swing.event.*;
0054 import javax.swing.plaf.*;
0055 import javax.swing.plaf.basic.*;
0056 import javax.accessibility.*;
0057
0058 import java.lang.ref.WeakReference;
0059
0060 /**
0061 * An implementation of a menu -- a popup window containing
0062 * <code>JMenuItem</code>s that
0063 * is displayed when the user selects an item on the <code>JMenuBar</code>.
0064 * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can
0065 * also contain <code>JSeparator</code>s.
0066 * <p>
0067 * In essence, a menu is a button with an associated <code>JPopupMenu</code>.
0068 * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the
0069 * "button" is on the <code>JMenuBar</code>, the menu is a top-level window.
0070 * If the "button" is another menu item, then the <code>JPopupMenu</code> is
0071 * "pull-right" menu.
0072 * <p>
0073 * Menus can be configured, and to some degree controlled, by
0074 * <code><a href="Action.html">Action</a></code>s. Using an
0075 * <code>Action</code> with a menu has many benefits beyond directly
0076 * configuring a menu. Refer to <a href="Action.html#buttonActions">
0077 * Swing Components Supporting <code>Action</code></a> for more
0078 * details, and you can find more information in <a
0079 * href="http://java.sun.com/docs/books/tutorial/uiswing/misc/action.html">How
0080 * to Use Actions</a>, a section in <em>The Java Tutorial</em>.
0081 * <p>
0082 * For information and examples of using menus see
0083 * <a href="http://java.sun.com/doc/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
0084 * a section in <em>The Java Tutorial.</em>
0085 * <p>
0086 * <strong>Warning:</strong> Swing is not thread safe. For more
0087 * information see <a
0088 * href="package-summary.html#threading">Swing's Threading
0089 * Policy</a>.
0090 * <p>
0091 * <strong>Warning:</strong>
0092 * Serialized objects of this class will not be compatible with
0093 * future Swing releases. The current serialization support is
0094 * appropriate for short term storage or RMI between applications running
0095 * the same version of Swing. As of 1.4, support for long term storage
0096 * of all JavaBeans<sup><font size="-2">TM</font></sup>
0097 * has been added to the <code>java.beans</code> package.
0098 * Please see {@link java.beans.XMLEncoder}.
0099 *
0100 * @beaninfo
0101 * attribute: isContainer true
0102 * description: A popup window containing menu items displayed in a menu bar.
0103 *
0104 * @version 1.187 05/05/07
0105 * @author Georges Saab
0106 * @author David Karlton
0107 * @author Arnaud Weber
0108 * @see JMenuItem
0109 * @see JSeparator
0110 * @see JMenuBar
0111 * @see JPopupMenu
0112 */
0113 public class JMenu extends JMenuItem implements Accessible, MenuElement {
0114 /**
0115 * @see #getUIClassID
0116 * @see #readObject
0117 */
0118 private static final String uiClassID = "MenuUI";
0119
0120 /*
0121 * The popup menu portion of the menu.
0122 */
0123 private JPopupMenu popupMenu;
0124
0125 /*
0126 * The button's model listeners. Default is <code>null</code>.
0127 */
0128 private ChangeListener menuChangeListener = null;
0129
0130 /*
0131 * Only one <code>MenuEvent</code> is needed for each menu since the
0132 * event's only state is the source property. The source of events
0133 * generated is always "this". Default is <code>null</code>.
0134 */
0135 private MenuEvent menuEvent = null;
0136
0137 /* Registry of listeners created for <code>Action-JMenuItem</code>
0138 * linkage. This is needed so that references can
0139 * be cleaned up at remove time to allow garbage collection
0140 * Default is <code>null</code>.
0141 */
0142 private static Hashtable listenerRegistry = null;
0143
0144 /*
0145 * Used by the look and feel (L&F) code to handle
0146 * implementation specific menu behaviors.
0147 */
0148 private int delay;
0149
0150 /*
0151 * Location of the popup component. Location is <code>null</code>
0152 * if it was not customized by <code>setMenuLocation</code>
0153 */
0154 private Point customMenuLocation = null;
0155
0156 /* Diagnostic aids -- should be false for production builds. */
0157 private static final boolean TRACE = false; // trace creates and disposes
0158 private static final boolean VERBOSE = false; // show reuse hits/misses
0159 private static final boolean DEBUG = false; // show bad params, misc.
0160
0161 /**
0162 * Constructs a new <code>JMenu</code> with no text.
0163 */
0164 public JMenu() {
0165 this ("");
0166 }
0167
0168 /**
0169 * Constructs a new <code>JMenu</code> with the supplied string
0170 * as its text.
0171 *
0172 * @param s the text for the menu label
0173 */
0174 public JMenu(String s) {
0175 super (s);
0176 }
0177
0178 /**
0179 * Constructs a menu whose properties are taken from the
0180 * <code>Action</code> supplied.
0181 * @param a an <code>Action</code>
0182 *
0183 * @since 1.3
0184 */
0185 public JMenu(Action a) {
0186 this ();
0187 setAction(a);
0188 }
0189
0190 /**
0191 * Constructs a new <code>JMenu</code> with the supplied string as
0192 * its text and specified as a tear-off menu or not.
0193 *
0194 * @param s the text for the menu label
0195 * @param b can the menu be torn off (not yet implemented)
0196 */
0197 public JMenu(String s, boolean b) {
0198 this (s);
0199 }
0200
0201 /**
0202 * Overriden to do nothing. We want JMenu to be focusable, but
0203 * <code>JMenuItem</code> doesn't want to be, thus we override this
0204 * do nothing. We don't invoke <code>setFocusable(true)</code> after
0205 * super's constructor has completed as this has the side effect that
0206 * <code>JMenu</code> will be considered traversable via the
0207 * keyboard, which we don't want. Making a Component traversable by
0208 * the keyboard after invoking <code>setFocusable(true)</code> is OK,
0209 * as <code>setFocusable</code> is new API
0210 * and is speced as such, but internally we don't want to use it like
0211 * this else we change the keyboard traversability.
0212 */
0213 void initFocusability() {
0214 }
0215
0216 /**
0217 * Resets the UI property with a value from the current look and feel.
0218 *
0219 * @see JComponent#updateUI
0220 */
0221 public void updateUI() {
0222 setUI((MenuItemUI) UIManager.getUI(this ));
0223
0224 if (popupMenu != null) {
0225 popupMenu.setUI((PopupMenuUI) UIManager.getUI(popupMenu));
0226 }
0227
0228 }
0229
0230 /**
0231 * Returns the name of the L&F class that renders this component.
0232 *
0233 * @return the string "MenuUI"
0234 * @see JComponent#getUIClassID
0235 * @see UIDefaults#getUI
0236 */
0237 public String getUIClassID() {
0238 return uiClassID;
0239 }
0240
0241 // public void repaint(long tm, int x, int y, int width, int height) {
0242 // Thread.currentThread().dumpStack();
0243 // super.repaint(tm,x,y,width,height);
0244 // }
0245
0246 /**
0247 * Sets the data model for the "menu button" -- the label
0248 * that the user clicks to open or close the menu.
0249 *
0250 * @param newModel the <code>ButtonModel</code>
0251 * @see #getModel
0252 * @beaninfo
0253 * description: The menu's model
0254 * bound: true
0255 * expert: true
0256 * hidden: true
0257 */
0258 public void setModel(ButtonModel newModel) {
0259 ButtonModel oldModel = getModel();
0260
0261 super .setModel(newModel);
0262
0263 if (oldModel != null && menuChangeListener != null) {
0264 oldModel.removeChangeListener(menuChangeListener);
0265 menuChangeListener = null;
0266 }
0267
0268 model = newModel;
0269
0270 if (newModel != null) {
0271 menuChangeListener = createMenuChangeListener();
0272 newModel.addChangeListener(menuChangeListener);
0273 }
0274 }
0275
0276 /**
0277 * Returns true if the menu is currently selected (highlighted).
0278 *
0279 * @return true if the menu is selected, else false
0280 */
0281 public boolean isSelected() {
0282 return getModel().isSelected();
0283 }
0284
0285 /**
0286 * Sets the selection status of the menu.
0287 *
0288 * @param b true to select (highlight) the menu; false to de-select
0289 * the menu
0290 * @beaninfo
0291 * description: When the menu is selected, its popup child is shown.
0292 * expert: true
0293 * hidden: true
0294 */
0295 public void setSelected(boolean b) {
0296 ButtonModel model = getModel();
0297 boolean oldValue = model.isSelected();
0298
0299 // TIGER - 4840653
0300 // Removed code which fired an AccessibleState.SELECTED
0301 // PropertyChangeEvent since this resulted in two
0302 // identical events being fired since
0303 // AbstractButton.fireItemStateChanged also fires the
0304 // same event. This caused screen readers to speak the
0305 // name of the item twice.
0306
0307 if (b != model.isSelected()) {
0308 getModel().setSelected(b);
0309 }
0310 }
0311
0312 /**
0313 * Returns true if the menu's popup window is visible.
0314 *
0315 * @return true if the menu is visible, else false
0316 */
0317 public boolean isPopupMenuVisible() {
0318 ensurePopupMenuCreated();
0319 return popupMenu.isVisible();
0320 }
0321
0322 /**
0323 * Sets the visibility of the menu's popup. If the menu is
0324 * not enabled, this method will have no effect.
0325 *
0326 * @param b a boolean value -- true to make the menu visible,
0327 * false to hide it
0328 * @beaninfo
0329 * description: The popup menu's visibility
0330 * expert: true
0331 * hidden: true
0332 */
0333 public void setPopupMenuVisible(boolean b) {
0334 if (DEBUG) {
0335 System.out.println("in JMenu.setPopupMenuVisible " + b);
0336 // Thread.dumpStack();
0337 }
0338
0339 boolean isVisible = isPopupMenuVisible();
0340 if (b != isVisible && (isEnabled() || !b)) {
0341 ensurePopupMenuCreated();
0342 if ((b == true) && isShowing()) {
0343 // Set location of popupMenu (pulldown or pullright)
0344 Point p = getCustomMenuLocation();
0345 if (p == null) {
0346 p = getPopupMenuOrigin();
0347 }
0348 getPopupMenu().show(this , p.x, p.y);
0349 } else {
0350 getPopupMenu().setVisible(false);
0351 }
0352 }
0353 }
0354
0355 /**
0356 * Computes the origin for the <code>JMenu</code>'s popup menu.
0357 * This method uses Look and Feel properties named
0358 * <code>Menu.menuPopupOffsetX</code>,
0359 * <code>Menu.menuPopupOffsetY</code>,
0360 * <code>Menu.submenuPopupOffsetX</code>, and
0361 * <code>Menu.submenuPopupOffsetY</code>
0362 * to adjust the exact location of popup.
0363 *
0364 * @return a <code>Point</code> in the coordinate space of the
0365 * menu which should be used as the origin
0366 * of the <code>JMenu</code>'s popup menu
0367 *
0368 * @since 1.3
0369 */
0370 protected Point getPopupMenuOrigin() {
0371 int x = 0;
0372 int y = 0;
0373 JPopupMenu pm = getPopupMenu();
0374 // Figure out the sizes needed to caclulate the menu position
0375 Dimension s = getSize();
0376 Dimension pmSize = pm.getSize();
0377 // For the first time the menu is popped up,
0378 // the size has not yet been initiated
0379 if (pmSize.width == 0) {
0380 pmSize = pm.getPreferredSize();
0381 }
0382 Point position = getLocationOnScreen();
0383 Toolkit toolkit = Toolkit.getDefaultToolkit();
0384 GraphicsConfiguration gc = getGraphicsConfiguration();
0385 Rectangle screenBounds = new Rectangle(toolkit.getScreenSize());
0386 GraphicsEnvironment ge = GraphicsEnvironment
0387 .getLocalGraphicsEnvironment();
0388 GraphicsDevice[] gd = ge.getScreenDevices();
0389 for (int i = 0; i < gd.length; i++) {
0390 if (gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
0391 GraphicsConfiguration dgc = gd[i]
0392 .getDefaultConfiguration();
0393 if (dgc.getBounds().contains(position)) {
0394 gc = dgc;
0395 break;
0396 }
0397 }
0398 }
0399
0400 if (gc != null) {
0401 screenBounds = gc.getBounds();
0402 // take screen insets (e.g. taskbar) into account
0403 Insets screenInsets = toolkit.getScreenInsets(gc);
0404
0405 screenBounds.width -= Math.abs(screenInsets.left
0406 + screenInsets.right);
0407 screenBounds.height -= Math.abs(screenInsets.top
0408 + screenInsets.bottom);
0409 position.x -= Math.abs(screenInsets.left);
0410 position.y -= Math.abs(screenInsets.top);
0411 }
0412
0413 Container parent = getParent();
0414 if (parent instanceof JPopupMenu) {
0415 // We are a submenu (pull-right)
0416 int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");
0417 int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");
0418
0419 if (SwingUtilities.isLeftToRight(this )) {
0420 // First determine x:
0421 x = s.width + xOffset; // Prefer placement to the right
0422 if (position.x + x + pmSize.width >= screenBounds.width
0423 + screenBounds.x
0424 &&
0425 // popup doesn't fit - place it wherever there's more room
0426 screenBounds.width - s.width < 2 * (position.x - screenBounds.x)) {
0427
0428 x = 0 - xOffset - pmSize.width;
0429 }
0430 } else {
0431 // First determine x:
0432 x = 0 - xOffset - pmSize.width; // Prefer placement to the left
0433 if (position.x + x < screenBounds.x &&
0434 // popup doesn't fit - place it wherever there's more room
0435 screenBounds.width - s.width > 2 * (position.x - screenBounds.x)) {
0436
0437 x = s.width + xOffset;
0438 }
0439 }
0440 // Then the y:
0441 y = yOffset; // Prefer dropping down
0442 if (position.y + y + pmSize.height >= screenBounds.height
0443 + screenBounds.y
0444 &&
0445 // popup doesn't fit - place it wherever there's more room
0446 screenBounds.height - s.height < 2 * (position.y - screenBounds.y)) {
0447
0448 y = s.height - yOffset - pmSize.height;
0449 }
0450 } else {
0451 // We are a toplevel menu (pull-down)
0452 int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");
0453 int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");
0454
0455 if (SwingUtilities.isLeftToRight(this )) {
0456 // First determine the x:
0457 x = xOffset; // Extend to the right
0458 if (position.x + x + pmSize.width >= screenBounds.width
0459 + screenBounds.x
0460 &&
0461 // popup doesn't fit - place it wherever there's more room
0462 screenBounds.width - s.width < 2 * (position.x - screenBounds.x)) {
0463
0464 x = s.width - xOffset - pmSize.width;
0465 }
0466 } else {
0467 // First determine the x:
0468 x = s.width - xOffset - pmSize.width; // Extend to the left
0469 if (position.x + x < screenBounds.x &&
0470 // popup doesn't fit - place it wherever there's more room
0471 screenBounds.width - s.width > 2 * (position.x - screenBounds.x)) {
0472
0473 x = xOffset;
0474 }
0475 }
0476 // Then the y:
0477 y = s.height + yOffset; // Prefer dropping down
0478 if (position.y + y + pmSize.height >= screenBounds.height &&
0479 // popup doesn't fit - place it wherever there's more room
0480 screenBounds.height - s.height < 2 * (position.y - screenBounds.y)) {
0481
0482 y = 0 - yOffset - pmSize.height; // Otherwise drop 'up'
0483 }
0484 }
0485 return new Point(x, y);
0486 }
0487
0488 /**
0489 * Returns the suggested delay, in milliseconds, before submenus
0490 * are popped up or down.
0491 * Each look and feel (L&F) may determine its own policy for
0492 * observing the <code>delay</code> property.
0493 * In most cases, the delay is not observed for top level menus
0494 * or while dragging. The default for <code>delay</code> is 0.
0495 * This method is a property of the look and feel code and is used
0496 * to manage the idiosyncracies of the various UI implementations.
0497 *
0498 *
0499 * @return the <code>delay</code> property
0500 */
0501 public int getDelay() {
0502 return delay;
0503 }
0504
0505 /**
0506 * Sets the suggested delay before the menu's <code>PopupMenu</code>
0507 * is popped up or down. Each look and feel (L&F) may determine
0508 * it's own policy for observing the delay property. In most cases,
0509 * the delay is not observed for top level menus or while dragging.
0510 * This method is a property of the look and feel code and is used
0511 * to manage the idiosyncracies of the various UI implementations.
0512 *
0513 * @param d the number of milliseconds to delay
0514 * @exception IllegalArgumentException if <code>d</code>
0515 * is less than 0
0516 * @beaninfo
0517 * description: The delay between menu selection and making the popup menu visible
0518 * expert: true
0519 */
0520 public void setDelay(int d) {
0521 if (d < 0)
0522 throw new IllegalArgumentException(
0523 "Delay must be a positive integer");
0524
0525 delay = d;
0526 }
0527
0528 /**
0529 * The window-closing listener for the popup.
0530 *
0531 * @see WinListener
0532 */
0533 protected WinListener popupListener;
0534
0535 private void ensurePopupMenuCreated() {
0536 if (popupMenu == null) {
0537 final JMenu this Menu = this ;
0538 this .popupMenu = new JPopupMenu();
0539 popupMenu.setInvoker(this );
0540 popupListener = createWinListener(popupMenu);
0541 }
0542 }
0543
0544 /*
0545 * Return the customized location of the popup component.
0546 */
0547 private Point getCustomMenuLocation() {
0548 return customMenuLocation;
0549 }
0550
0551 /**
0552 * Sets the location of the popup component.
0553 *
0554 * @param x the x coordinate of the popup's new position
0555 * @param y the y coordinate of the popup's new position
0556 */
0557 public void setMenuLocation(int x, int y) {
0558 customMenuLocation = new Point(x, y);
0559 if (popupMenu != null)
0560 popupMenu.setLocation(x, y);
0561 }
0562
0563 /**
0564 * Appends a menu item to the end of this menu.
0565 * Returns the menu item added.
0566 *
0567 * @param menuItem the <code>JMenuitem</code> to be added
0568 * @return the <code>JMenuItem</code> added
0569 */
0570 public JMenuItem add(JMenuItem menuItem) {
0571 ensurePopupMenuCreated();
0572 return popupMenu.add(menuItem);
0573 }
0574
0575 /**
0576 * Appends a component to the end of this menu.
0577 * Returns the component added.
0578 *
0579 * @param c the <code>Component</code> to add
0580 * @return the <code>Component</code> added
0581 */
0582 public Component add(Component c) {
0583 ensurePopupMenuCreated();
0584 popupMenu.add(c);
0585 return c;
0586 }
0587
0588 /**
0589 * Adds the specified component to this container at the given
0590 * position. If <code>index</code> equals -1, the component will
0591 * be appended to the end.
0592 * @param c the <code>Component</code> to add
0593 * @param index the position at which to insert the component
0594 * @return the <code>Component</code> added
0595 * @see #remove
0596 * @see java.awt.Container#add(Component, int)
0597 */
0598 public Component add(Component c, int index) {
0599 ensurePopupMenuCreated();
0600 popupMenu.add(c, index);
0601 return c;
0602 }
0603
0604 /**
0605 * Creates a new menu item with the specified text and appends
0606 * it to the end of this menu.
0607 *
0608 * @param s the string for the menu item to be added
0609 */
0610 public JMenuItem add(String s) {
0611 return add(new JMenuItem(s));
0612 }
0613
0614 /**
0615 * Creates a new menu item attached to the specified
0616 * <code>Action</code> object and appends it to the end of this menu.
0617 *
0618 * @param a the <code>Action</code> for the menu item to be added
0619 * @see Action
0620 */
0621 public JMenuItem add(Action a) {
0622 JMenuItem mi = createActionComponent(a);
0623 mi.setAction(a);
0624 add(mi);
0625 return mi;
0626 }
0627
0628 /**
0629 * Factory method which creates the <code>JMenuItem</code> for
0630 * <code>Action</code>s added to the <code>JMenu</code>.
0631 *
0632 * @param a the <code>Action</code> for the menu item to be added
0633 * @return the new menu item
0634 * @see Action
0635 *
0636 * @since 1.3
0637 */
0638 protected JMenuItem createActionComponent(Action a) {
0639 JMenuItem mi = new JMenuItem() {
0640 protected PropertyChangeListener createActionPropertyChangeListener(
0641 Action a) {
0642 PropertyChangeListener pcl = createActionChangeListener(this );
0643 if (pcl == null) {
0644 pcl = super .createActionPropertyChangeListener(a);
0645 }
0646 return pcl;
0647 }
0648 };
0649 mi.setHorizontalTextPosition(JButton.TRAILING);
0650 mi.setVerticalTextPosition(JButton.CENTER);
0651 return mi;
0652 }
0653
0654 /**
0655 * Returns a properly configured <code>PropertyChangeListener</code>
0656 * which updates the control as changes to the <code>Action</code> occur.
0657 */
0658 protected PropertyChangeListener createActionChangeListener(
0659 JMenuItem b) {
0660 return b.createActionPropertyChangeListener0(b.getAction());
0661 }
0662
0663 /**
0664 * Appends a new separator to the end of the menu.
0665 */
0666 public void addSeparator() {
0667 ensurePopupMenuCreated();
0668 popupMenu.addSeparator();
0669 }
0670
0671 /**
0672 * Inserts a new menu item with the specified text at a
0673 * given position.
0674 *
0675 * @param s the text for the menu item to add
0676 * @param pos an integer specifying the position at which to add the
0677 * new menu item
0678 * @exception IllegalArgumentException when the value of
0679 * <code>pos</code> < 0
0680 */
0681 public void insert(String s, int pos) {
0682 if (pos < 0) {
0683 throw new IllegalArgumentException("index less than zero.");
0684 }
0685
0686 ensurePopupMenuCreated();
0687 popupMenu.insert(new JMenuItem(s), pos);
0688 }
0689
0690 /**
0691 * Inserts the specified <code>JMenuitem</code> at a given position.
0692 *
0693 * @param mi the <code>JMenuitem</code> to add
0694 * @param pos an integer specifying the position at which to add the
0695 * new <code>JMenuitem</code>
0696 * @return the new menu item
0697 * @exception IllegalArgumentException if the value of
0698 * <code>pos</code> < 0
0699 */
0700 public JMenuItem insert(JMenuItem mi, int pos) {
0701 if (pos < 0) {
0702 throw new IllegalArgumentException("index less than zero.");
0703 }
0704 ensurePopupMenuCreated();
0705 popupMenu.insert(mi, pos);
0706 return mi;
0707 }
0708
0709 /**
0710 * Inserts a new menu item attached to the specified <code>Action</code>
0711 * object at a given position.
0712 *
0713 * @param a the <code>Action</code> object for the menu item to add
0714 * @param pos an integer specifying the position at which to add the
0715 * new menu item
0716 * @exception IllegalArgumentException if the value of
0717 * <code>pos</code> < 0
0718 */
0719 public JMenuItem insert(Action a, int pos) {
0720 if (pos < 0) {
0721 throw new IllegalArgumentException("index less than zero.");
0722 }
0723
0724 ensurePopupMenuCreated();
0725 JMenuItem mi = new JMenuItem(a);
0726 mi.setHorizontalTextPosition(JButton.TRAILING);
0727 mi.setVerticalTextPosition(JButton.CENTER);
0728 popupMenu.insert(mi, pos);
0729 return mi;
0730 }
0731
0732 /**
0733 * Inserts a separator at the specified position.
0734 *
0735 * @param index an integer specifying the position at which to
0736 * insert the menu separator
0737 * @exception IllegalArgumentException if the value of
0738 * <code>index</code> < 0
0739 */
0740 public void insertSeparator(int index) {
0741 if (index < 0) {
0742 throw new IllegalArgumentException("index less than zero.");
0743 }
0744
0745 ensurePopupMenuCreated();
0746 popupMenu.insert(new JPopupMenu.Separator(), index);
0747 }
0748
0749 /**
0750 * Returns the <code>JMenuItem</code> at the specified position.
0751 * If the component at <code>pos</code> is not a menu item,
0752 * <code>null</code> is returned.
0753 * This method is included for AWT compatibility.
0754 *
0755 * @param pos an integer specifying the position
0756 * @exception IllegalArgumentException if the value of
0757 * <code>pos</code> < 0
0758 * @return the menu item at the specified position; or <code>null</code>
0759 * if the item as the specified position is not a menu item
0760 */
0761 public JMenuItem getItem(int pos) {
0762 if (pos < 0) {
0763 throw new IllegalArgumentException("index less than zero.");
0764 }
0765
0766 Component c = getMenuComponent(pos);
0767 if (c instanceof JMenuItem) {
0768 JMenuItem mi = (JMenuItem) c;
0769 return mi;
0770 }
0771
0772 // 4173633
0773 return null;
0774 }
0775
0776 /**
0777 * Returns the number of items on the menu, including separators.
0778 * This method is included for AWT compatibility.
0779 *
0780 * @return an integer equal to the number of items on the menu
0781 * @see #getMenuComponentCount
0782 */
0783 public int getItemCount() {
0784 return getMenuComponentCount();
0785 }
0786
0787 /**
0788 * Returns true if the menu can be torn off. This method is not
0789 * yet implemented.
0790 *
0791 * @return true if the menu can be torn off, else false
0792 * @exception Error if invoked -- this method is not yet implemented
0793 */
0794 public boolean isTearOff() {
0795 throw new Error("boolean isTearOff() {} not yet implemented");
0796 }
0797
0798 /**
0799 * Removes the specified menu item from this menu. If there is no
0800 * popup menu, this method will have no effect.
0801 *
0802 * @param item the <code>JMenuItem</code> to be removed from the menu
0803 */
0804 public void remove(JMenuItem item) {
0805 if (popupMenu != null)
0806 popupMenu.remove(item);
0807 }
0808
0809 /**
0810 * Removes the menu item at the specified index from this menu.
0811 *
0812 * @param pos the position of the item to be removed
0813 * @exception IllegalArgumentException if the value of
0814 * <code>pos</code> < 0, or if <code>pos</code>
0815 * is greater than the number of menu items
0816 */
0817 public void remove(int pos) {
0818 if (pos < 0) {
0819 throw new IllegalArgumentException("index less than zero.");
0820 }
0821 if (pos > getItemCount()) {
0822 throw new IllegalArgumentException(
0823 "index greater than the number of items.");
0824 }
0825 if (popupMenu != null)
0826 popupMenu.remove(pos);
0827 }
0828
0829 /**
0830 * Removes the component <code>c</code> from this menu.
0831 *
0832 * @param c the component to be removed
0833 */
0834 public void remove(Component c) {
0835 if (popupMenu != null)
0836 popupMenu.remove(c);
0837 }
0838
0839 /**
0840 * Removes all menu items from this menu.
0841 */
0842 public void removeAll() {
0843 if (popupMenu != null)
0844 popupMenu.removeAll();
0845 }
0846
0847 /**
0848 * Returns the number of components on the menu.
0849 *
0850 * @return an integer containing the number of components on the menu
0851 */
0852 public int getMenuComponentCount() {
0853 int componentCount = 0;
0854 if (popupMenu != null)
0855 componentCount = popupMenu.getComponentCount();
0856 return componentCount;
0857 }
0858
0859 /**
0860 * Returns the component at position <code>n</code>.
0861 *
0862 * @param n the position of the component to be returned
0863 * @return the component requested, or <code>null</code>
0864 * if there is no popup menu
0865 *
0866 */
0867 public Component getMenuComponent(int n) {
0868 if (popupMenu != null)
0869 return popupMenu.getComponent(n);
0870
0871 return null;
0872 }
0873
0874 /**
0875 * Returns an array of <code>Component</code>s of the menu's
0876 * subcomponents. Note that this returns all <code>Component</code>s
0877 * in the popup menu, including separators.
0878 *
0879 * @return an array of <code>Component</code>s or an empty array
0880 * if there is no popup menu
0881 */
0882 public Component[] getMenuComponents() {
0883 if (popupMenu != null)
0884 return popupMenu.getComponents();
0885
0886 return new Component[0];
0887 }
0888
0889 /**
0890 * Returns true if the menu is a 'top-level menu', that is, if it is
0891 * the direct child of a menubar.
0892 *
0893 * @return true if the menu is activated from the menu bar;
0894 * false if the menu is activated from a menu item
0895 * on another menu
0896 */
0897 public boolean isTopLevelMenu() {
0898 if (getParent() instanceof JMenuBar)
0899 return true;
0900
0901 return false;
0902 }
0903
0904 /**
0905 * Returns true if the specified component exists in the
0906 * submenu hierarchy.
0907 *
0908 * @param c the <code>Component</code> to be tested
0909 * @return true if the <code>Component</code> exists, false otherwise
0910 */
0911 public boolean isMenuComponent(Component c) {
0912 // Are we in the MenuItem part of the menu
0913 if (c == this )
0914 return true;
0915 // Are we in the PopupMenu?
0916 if (c instanceof JPopupMenu) {
0917 JPopupMenu comp = (JPopupMenu) c;
0918 if (comp == this .getPopupMenu())
0919 return true;
0920 }
0921 // Are we in a Component on the PopupMenu
0922 int ncomponents = this .getMenuComponentCount();
0923 Component[] component = this .getMenuComponents();
0924 for (int i = 0; i < ncomponents; i++) {
0925 Component comp = component[i];
0926 // Are we in the current component?
0927 if (comp == c)
0928 return true;
0929 // Hmmm, what about Non-menu containers?
0930
0931 // Recursive call for the Menu case
0932 if (comp instanceof JMenu) {
0933 JMenu subMenu = (JMenu) comp;
0934 if (subMenu.isMenuComponent(c))
0935 return true;
0936 }
0937 }
0938 return false;
0939 }
0940
0941 /*
0942 * Returns a point in the coordinate space of this menu's popupmenu
0943 * which corresponds to the point <code>p</code> in the menu's
0944 * coordinate space.
0945 *
0946 * @param p the point to be translated
0947 * @return the point in the coordinate space of this menu's popupmenu
0948 */
0949 private Point translateToPopupMenu(Point p) {
0950 return translateToPopupMenu(p.x, p.y);
0951 }
0952
0953 /*
0954 * Returns a point in the coordinate space of this menu's popupmenu
0955 * which corresponds to the point (x,y) in the menu's coordinate space.
0956 *
0957 * @param x the x coordinate of the point to be translated
0958 * @param y the y coordinate of the point to be translated
0959 * @return the point in the coordinate space of this menu's popupmenu
0960 */
0961 private Point translateToPopupMenu(int x, int y) {
0962 int newX;
0963 int newY;
0964
0965 if (getParent() instanceof JPopupMenu) {
0966 newX = x - getSize().width;
0967 newY = y;
0968 } else {
0969 newX = x;
0970 newY = y - getSize().height;
0971 }
0972
0973 return new Point(newX, newY);
0974 }
0975
0976 /**
0977 * Returns the popupmenu associated with this menu. If there is
0978 * no popupmenu, it will create one.
0979 */
0980 public JPopupMenu getPopupMenu() {
0981 ensurePopupMenuCreated();
0982 return popupMenu;
0983 }
0984
0985 /**
0986 * Adds a listener for menu events.
0987 *
0988 * @param l the listener to be added
0989 */
0990 public void addMenuListener(MenuListener l) {
0991 listenerList.add(MenuListener.class, l);
0992 }
0993
0994 /**
0995 * Removes a listener for menu events.
0996 *
0997 * @param l the listener to be removed
0998 */
0999 public void removeMenuListener(MenuListener l) {
1000 listenerList.remove(MenuListener.class, l);
1001 }
1002
1003 /**
1004 * Returns an array of all the <code>MenuListener</code>s added
1005 * to this JMenu with addMenuListener().
1006 *
1007 * @return all of the <code>MenuListener</code>s added or an empty
1008 * array if no listeners have been added
1009 * @since 1.4
1010 */
1011 public MenuListener[] getMenuListeners() {
1012 return (MenuListener[]) listenerList
1013 .getListeners(MenuListener.class);
1014 }
1015
1016 /**
1017 * Notifies all listeners that have registered interest for
1018 * notification on this event type. The event instance
1019 * is created lazily.
1020 *
1021 * @exception Error if there is a <code>null</code> listener
1022 * @see EventListenerList
1023 */
1024 protected void fireMenuSelected() {
1025 if (DEBUG) {
1026 System.out.println("In JMenu.fireMenuSelected");
1027 }
1028 // Guaranteed to return a non-null array
1029 Object[] listeners = listenerList.getListenerList();
1030 // Process the listeners last to first, notifying
1031 // those that are interested in this event
1032 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1033 if (listeners[i] == MenuListener.class) {
1034 if (listeners[i + 1] == null) {
1035 throw new Error(getText()
1036 + " has a NULL Listener!! " + i);
1037 } else {
1038 // Lazily create the event:
1039 if (menuEvent == null)
1040 menuEvent = new MenuEvent(this );
1041 ((MenuListener) listeners[i + 1])
1042 .menuSelected(menuEvent);
1043 }
1044 }
1045 }
1046 }
1047
1048 /**
1049 * Notifies all listeners that have registered interest for
1050 * notification on this event type. The event instance
1051 * is created lazily.
1052 *
1053 * @exception Error if there is a <code>null</code> listener
1054 * @see EventListenerList
1055 */
1056 protected void fireMenuDeselected() {
1057 if (DEBUG) {
1058 System.out.println("In JMenu.fireMenuDeselected");
1059 }
1060 // Guaranteed to return a non-null array
1061 Object[] listeners = listenerList.getListenerList();
1062 // Process the listeners last to first, notifying
1063 // those that are interested in this event
1064 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1065 if (listeners[i] == MenuListener.class) {
1066 if (listeners[i + 1] == null) {
1067 throw new Error(getText()
1068 + " has a NULL Listener!! " + i);
1069 } else {
1070 // Lazily create the event:
1071 if (menuEvent == null)
1072 menuEvent = new MenuEvent(this );
1073 ((MenuListener) listeners[i + 1])
1074 .menuDeselected(menuEvent);
1075 }
1076 }
1077 }
1078 }
1079
1080 /**
1081 * Notifies all listeners that have registered interest for
1082 * notification on this event type. The event instance
1083 * is created lazily.
1084 *
1085 * @exception Error if there is a <code>null</code> listener
1086 * @see EventListenerList
1087 */
1088 protected void fireMenuCanceled() {
1089 if (DEBUG) {
1090 System.out.println("In JMenu.fireMenuCanceled");
1091 }
1092 // Guaranteed to return a non-null array
1093 Object[] listeners = listenerList.getListenerList();
1094 // Process the listeners last to first, notifying
1095 // those that are interested in this event
1096 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1097 if (listeners[i] == MenuListener.class) {
1098 if (listeners[i + 1] == null) {
1099 throw new Error(getText()
1100 + " has a NULL Listener!! " + i);
1101 } else {
1102 // Lazily create the event:
1103 if (menuEvent == null)
1104 menuEvent = new MenuEvent(this );
1105 ((MenuListener) listeners[i + 1])
1106 .menuCanceled(menuEvent);
1107 }
1108 }
1109 }
1110 }
1111
1112 // Overriden to do nothing, JMenu doesn't support an accelerator
1113 void configureAcceleratorFromAction(Action a) {
1114 }
1115
1116 class MenuChangeListener implements ChangeListener, Serializable {
1117 boolean isSelected = false;
1118
1119 public void stateChanged(ChangeEvent e) {
1120 ButtonModel model = (ButtonModel) e.getSource();
1121 boolean modelSelected = model.isSelected();
1122
1123 if (modelSelected != isSelected) {
1124 if (modelSelected == true) {
1125 fireMenuSelected();
1126 } else {
1127 fireMenuDeselected();
1128 }
1129 isSelected = modelSelected;
1130 }
1131 }
1132 }
1133
1134 private ChangeListener createMenuChangeListener() {
1135 return new MenuChangeListener();
1136 }
1137
1138 /**
1139 * Creates a window-closing listener for the popup.
1140 *
1141 * @param p the <code>JPopupMenu</code>
1142 * @return the new window-closing listener
1143 *
1144 * @see WinListener
1145 */
1146 protected WinListener createWinListener(JPopupMenu p) {
1147 return new WinListener(p);
1148 }
1149
1150 /**
1151 * A listener class that watches for a popup window closing.
1152 * When the popup is closing, the listener deselects the menu.
1153 * <p>
1154 * <strong>Warning:</strong>
1155 * Serialized objects of this class will not be compatible with
1156 * future Swing releases. The current serialization support is
1157 * appropriate for short term storage or RMI between applications running
1158 * the same version of Swing. As of 1.4, support for long term storage
1159 * of all JavaBeans<sup><font size="-2">TM</font></sup>
1160 * has been added to the <code>java.beans</code> package.
1161 * Please see {@link java.beans.XMLEncoder}.
1162 */
1163 protected class WinListener extends WindowAdapter implements
1164 Serializable {
1165 JPopupMenu popupMenu;
1166
1167 /**
1168 * Create the window listener for the specified popup.
1169 * @since 1.4
1170 */
1171 public WinListener(JPopupMenu p) {
1172 this .popupMenu = p;
1173 }
1174
1175 /**
1176 * Deselect the menu when the popup is closed from outside.
1177 */
1178 public void windowClosing(WindowEvent e) {
1179 setSelected(false);
1180 }
1181 }
1182
1183 /**
1184 * Messaged when the menubar selection changes to activate or
1185 * deactivate this menu.
1186 * Overrides <code>JMenuItem.menuSelectionChanged</code>.
1187 *
1188 * @param isIncluded true if this menu is active, false if
1189 * it is not
1190 */
1191 public void menuSelectionChanged(boolean isIncluded) {
1192 if (DEBUG) {
1193 System.out.println("In JMenu.menuSelectionChanged to "
1194 + isIncluded);
1195 }
1196 setSelected(isIncluded);
1197 }
1198
1199 /**
1200 * Returns an array of <code>MenuElement</code>s containing the submenu
1201 * for this menu component. If popup menu is <code>null</code> returns
1202 * an empty array. This method is required to conform to the
1203 * <code>MenuElement</code> interface. Note that since
1204 * <code>JSeparator</code>s do not conform to the <code>MenuElement</code>
1205 * interface, this array will only contain <code>JMenuItem</code>s.
1206 *
1207 * @return an array of <code>MenuElement</code> objects
1208 */
1209 public MenuElement[] getSubElements() {
1210 if (popupMenu == null)
1211 return new MenuElement[0];
1212 else {
1213 MenuElement result[] = new MenuElement[1];
1214 result[0] = popupMenu;
1215 return result;
1216 }
1217 }
1218
1219 // implements javax.swing.MenuElement
1220 /**
1221 * Returns the <code>java.awt.Component</code> used to
1222 * paint this <code>MenuElement</code>.
1223 * The returned component is used to convert events and detect if
1224 * an event is inside a menu component.
1225 */
1226 public Component getComponent() {
1227 return this ;
1228 }
1229
1230 /**
1231 * Sets the <code>ComponentOrientation</code> property of this menu
1232 * and all components contained within it. This includes all
1233 * components returned by {@link #getMenuComponents getMenuComponents}.
1234 *
1235 * @param o the new component orientation of this menu and
1236 * the components contained within it.
1237 * @exception NullPointerException if <code>orientation</code> is null.
1238 * @see java.awt.Component#setComponentOrientation
1239 * @see java.awt.Component#getComponentOrientation
1240 * @since 1.4
1241 */
1242 public void applyComponentOrientation(ComponentOrientation o) {
1243 super .applyComponentOrientation(o);
1244
1245 if (popupMenu != null) {
1246 int ncomponents = getMenuComponentCount();
1247 for (int i = 0; i < ncomponents; ++i) {
1248 getMenuComponent(i).applyComponentOrientation(o);
1249 }
1250 popupMenu.setComponentOrientation(o);
1251 }
1252 }
1253
1254 public void setComponentOrientation(ComponentOrientation o) {
1255 super .setComponentOrientation(o);
1256 if (popupMenu != null) {
1257 popupMenu.setComponentOrientation(o);
1258 }
1259 }
1260
1261 /**
1262 * <code>setAccelerator</code> is not defined for <code>JMenu</code>.
1263 * Use <code>setMnemonic</code> instead.
1264 * @param keyStroke the keystroke combination which will invoke
1265 * the <code>JMenuItem</code>'s actionlisteners
1266 * without navigating the menu hierarchy
1267 * @exception Error if invoked -- this method is not defined for JMenu.
1268 * Use <code>setMnemonic</code> instead
1269 *
1270 * @beaninfo
1271 * description: The keystroke combination which will invoke the JMenuItem's
1272 * actionlisteners without navigating the menu hierarchy
1273 * hidden: true
1274 */
1275 public void setAccelerator(KeyStroke keyStroke) {
1276 throw new Error(
1277 "setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
1278 }
1279
1280 /**
1281 * Processes key stroke events such as mnemonics and accelerators.
1282 *
1283 * @param evt the key event to be processed
1284 */
1285 protected void processKeyEvent(KeyEvent evt) {
1286 MenuSelectionManager.defaultManager().processKeyEvent(evt);
1287 if (evt.isConsumed())
1288 return;
1289
1290 super .processKeyEvent(evt);
1291 }
1292
1293 /**
1294 * Programmatically performs a "click". This overrides the method
1295 * <code>AbstractButton.doClick</code> in order to make the menu pop up.
1296 * @param pressTime indicates the number of milliseconds the
1297 * button was pressed for
1298 */
1299 public void doClick(int pressTime) {
1300 MenuElement me[] = buildMenuElementArray(this );
1301 MenuSelectionManager.defaultManager().setSelectedPath(me);
1302 }
1303
1304 /*
1305 * Build an array of menu elements - from <code>PopupMenu</code> to
1306 * the root <code>JMenuBar</code>.
1307 * @param leaf the leaf node from which to start building up the array
1308 * @return the array of menu items
1309 */
1310 private MenuElement[] buildMenuElementArray(JMenu leaf) {
1311 Vector elements = new Vector();
1312 Component current = leaf.getPopupMenu();
1313 JPopupMenu pop;
1314 JMenu menu;
1315 JMenuBar bar;
1316
1317 while (true) {
1318 if (current instanceof JPopupMenu) {
1319 pop = (JPopupMenu) current;
1320 elements.insertElementAt(pop, 0);
1321 current = pop.getInvoker();
1322 } else if (current instanceof JMenu) {
1323 menu = (JMenu) current;
1324 elements.insertElementAt(menu, 0);
1325 current = menu.getParent();
1326 } else if (current instanceof JMenuBar) {
1327 bar = (JMenuBar) current;
1328 elements.insertElementAt(bar, 0);
1329 MenuElement me[] = new MenuElement[elements.size()];
1330 elements.copyInto(me);
1331 return me;
1332 }
1333 }
1334 }
1335
1336 /**
1337 * See <code>readObject</code> and <code>writeObject</code> in
1338 * <code>JComponent</code> for more
1339 * information about serialization in Swing.
1340 */
1341 private void writeObject(ObjectOutputStream s) throws IOException {
1342 s.defaultWriteObject();
1343 if (getUIClassID().equals(uiClassID)) {
1344 byte count = JComponent.getWriteObjCounter(this );
1345 JComponent.setWriteObjCounter(this , --count);
1346 if (count == 0 && ui != null) {
1347 ui.installUI(this );
1348 }
1349 }
1350 }
1351
1352 /**
1353 * Returns a string representation of this <code>JMenu</code>. This
1354 * method is intended to be used only for debugging purposes, and the
1355 * content and format of the returned string may vary between
1356 * implementations. The returned string may be empty but may not
1357 * be <code>null</code>.
1358 *
1359 * @return a string representation of this JMenu.
1360 */
1361 protected String paramString() {
1362 return super .paramString();
1363 }
1364
1365 /////////////////
1366 // Accessibility support
1367 ////////////////
1368
1369 /**
1370 * Gets the AccessibleContext associated with this JMenu.
1371 * For JMenus, the AccessibleContext takes the form of an
1372 * AccessibleJMenu.
1373 * A new AccessibleJMenu instance is created if necessary.
1374 *
1375 * @return an AccessibleJMenu that serves as the
1376 * AccessibleContext of this JMenu
1377 */
1378 public AccessibleContext getAccessibleContext() {
1379 if (accessibleContext == null) {
1380 accessibleContext = new AccessibleJMenu();
1381 }
1382 return accessibleContext;
1383 }
1384
1385 /**
1386 * This class implements accessibility support for the
1387 * <code>JMenu</code> class. It provides an implementation of the
1388 * Java Accessibility API appropriate to menu user-interface elements.
1389 * <p>
1390 * <strong>Warning:</strong>
1391 * Serialized objects of this class will not be compatible with
1392 * future Swing releases. The current serialization support is
1393 * appropriate for short term storage or RMI between applications running
1394 * the same version of Swing. As of 1.4, support for long term storage
1395 * of all JavaBeans<sup><font size="-2">TM</font></sup>
1396 * has been added to the <code>java.beans</code> package.
1397 * Please see {@link java.beans.XMLEncoder}.
1398 */
1399 protected class AccessibleJMenu extends AccessibleJMenuItem
1400 implements AccessibleSelection {
1401
1402 /**
1403 * Returns the number of accessible children in the object. If all
1404 * of the children of this object implement Accessible, than this
1405 * method should return the number of children of this object.
1406 *
1407 * @return the number of accessible children in the object.
1408 */
1409 public int getAccessibleChildrenCount() {
1410 Component[] children = getMenuComponents();
1411 int count = 0;
1412 for (int j = 0; j < children.length; j++) {
1413 if (children[j] instanceof Accessible) {
1414 count++;
1415 }
1416 }
1417 return count;
1418 }
1419
1420 /**
1421 * Returns the nth Accessible child of the object.
1422 *
1423 * @param i zero-based index of child
1424 * @return the nth Accessible child of the object
1425 */
1426 public Accessible getAccessibleChild(int i) {
1427 Component[] children = getMenuComponents();
1428 int count = 0;
1429 for (int j = 0; j < children.length; j++) {
1430 if (children[j] instanceof Accessible) {
1431 if (count == i) {
1432 if (children[j] instanceof JComponent) {
1433 // FIXME: [[[WDW - probably should set this when
1434 // the component is added to the menu. I tried
1435 // to do this in most cases, but the separators
1436 // added by addSeparator are hard to get to.]]]
1437 AccessibleContext ac = ((Accessible) children[j])
1438 .getAccessibleContext();
1439 ac.setAccessibleParent(JMenu.this );
1440 }
1441 return (Accessible) children[j];
1442 } else {
1443 count++;
1444 }
1445 }
1446 }
1447 return null;
1448 }
1449
1450 /**
1451 * Get the role of this object.
1452 *
1453 * @return an instance of AccessibleRole describing the role of the
1454 * object
1455 * @see AccessibleRole
1456 */
1457 public AccessibleRole getAccessibleRole() {
1458 return AccessibleRole.MENU;
1459 }
1460
1461 /**
1462 * Get the AccessibleSelection associated with this object. In the
1463 * implementation of the Java Accessibility API for this class,
1464 * return this object, which is responsible for implementing the
1465 * AccessibleSelection interface on behalf of itself.
1466 *
1467 * @return this object
1468 */
1469 public AccessibleSelection getAccessibleSelection() {
1470 return this ;
1471 }
1472
1473 /**
1474 * Returns 1 if a sub-menu is currently selected in this menu.
1475 *
1476 * @return 1 if a menu is currently selected, else 0
1477 */
1478 public int getAccessibleSelectionCount() {
1479 MenuElement me[] = MenuSelectionManager.defaultManager()
1480 .getSelectedPath();
1481 if (me != null) {
1482 for (int i = 0; i < me.length; i++) {
1483 if (me[i] == JMenu.this ) { // this menu is selected
1484 if (i + 1 < me.length) {
1485 return 1;
1486 }
1487 }
1488 }
1489 }
1490 return 0;
1491 }
1492
1493 /**
1494 * Returns the currently selected sub-menu if one is selected,
1495 * otherwise null (there can only be one selection, and it can
1496 * only be a sub-menu, as otherwise menu items don't remain
1497 * selected).
1498 */
1499 public Accessible getAccessibleSelection(int i) {
1500 // if i is a sub-menu & popped, return it
1501 if (i < 0 || i >= getItemCount()) {
1502 return null;
1503 }
1504 MenuElement me[] = MenuSelectionManager.defaultManager()
1505 .getSelectedPath();
1506 if (me != null) {
1507 for (int j = 0; j < me.length; j++) {
1508 if (me[j] == JMenu.this ) { // this menu is selected
1509 // so find the next JMenuItem in the MenuElement
1510 // array, and return it!
1511 while (++j < me.length) {
1512 if (me[j] instanceof JMenuItem) {
1513 return (Accessible) me[j];
1514 }
1515 }
1516 }
1517 }
1518 }
1519 return null;
1520 }
1521
1522 /**
1523 * Returns true if the current child of this object is selected
1524 * (that is, if this child is a popped-up submenu).
1525 *
1526 * @param i the zero-based index of the child in this Accessible
1527 * object.
1528 * @see AccessibleContext#getAccessibleChild
1529 */
1530 public boolean isAccessibleChildSelected(int i) {
1531 // if i is a sub-menu and is pop-ed up, return true, else false
1532 MenuElement me[] = MenuSelectionManager.defaultManager()
1533 .getSelectedPath();
1534 if (me != null) {
1535 JMenuItem mi = JMenu.this .getItem(i);
1536 for (int j = 0; j < me.length; j++) {
1537 if (me[j] == mi) {
1538 return true;
1539 }
1540 }
1541 }
1542 return false;
1543 }
1544
1545 /**
1546 * Selects the <code>i</code>th menu in the menu.
1547 * If that item is a submenu,
1548 * it will pop up in response. If a different item is already
1549 * popped up, this will force it to close. If this is a sub-menu
1550 * that is already popped up (selected), this method has no
1551 * effect.
1552 *
1553 * @param i the index of the item to be selected
1554 * @see #getAccessibleStateSet
1555 */
1556 public void addAccessibleSelection(int i) {
1557 if (i < 0 || i >= getItemCount()) {
1558 return;
1559 }
1560 JMenuItem mi = getItem(i);
1561 if (mi != null) {
1562 if (mi instanceof JMenu) {
1563 MenuElement me[] = buildMenuElementArray((JMenu) mi);
1564 MenuSelectionManager.defaultManager()
1565 .setSelectedPath(me);
1566 } else {
1567 MenuSelectionManager.defaultManager()
1568 .setSelectedPath(null);
1569 }
1570 }
1571 }
1572
1573 /**
1574 * Removes the nth item from the selection. In general, menus
1575 * can only have one item within them selected at a time
1576 * (e.g. one sub-menu popped open).
1577 *
1578 * @param i the zero-based index of the selected item
1579 */
1580 public void removeAccessibleSelection(int i) {
1581 if (i < 0 || i >= getItemCount()) {
1582 return;
1583 }
1584 JMenuItem mi = getItem(i);
1585 if (mi != null && mi instanceof JMenu) {
1586 if (((JMenu) mi).isSelected()) {
1587 MenuElement old[] = MenuSelectionManager
1588 .defaultManager().getSelectedPath();
1589 MenuElement me[] = new MenuElement[old.length - 2];
1590 for (int j = 0; j < old.length - 2; j++) {
1591 me[j] = old[j];
1592 }
1593 MenuSelectionManager.defaultManager()
1594 .setSelectedPath(me);
1595 }
1596 }
1597 }
1598
1599 /**
1600 * Clears the selection in the object, so that nothing in the
1601 * object is selected. This will close any open sub-menu.
1602 */
1603 public void clearAccessibleSelection() {
1604 // if this menu is selected, reset selection to only go
1605 // to this menu; else do nothing
1606 MenuElement old[] = MenuSelectionManager.defaultManager()
1607 .getSelectedPath();
1608 if (old != null) {
1609 for (int j = 0; j < old.length; j++) {
1610 if (old[j] == JMenu.this ) { // menu is in the selection!
1611 MenuElement me[] = new MenuElement[j + 1];
1612 System.arraycopy(old, 0, me, 0, j);
1613 me[j] = JMenu.this .getPopupMenu();
1614 MenuSelectionManager.defaultManager()
1615 .setSelectedPath(me);
1616 }
1617 }
1618 }
1619 }
1620
1621 /**
1622 * Normally causes every selected item in the object to be selected
1623 * if the object supports multiple selections. This method
1624 * makes no sense in a menu bar, and so does nothing.
1625 */
1626 public void selectAllAccessibleSelection() {
1627 }
1628 } // inner class AccessibleJMenu
1629
1630 }
|