1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import java.awt.Component; 28 import java.awt.ComponentOrientation; 29 import java.awt.Container; 30 import java.awt.Dimension; 31 import java.awt.GraphicsConfiguration; 32 import java.awt.GraphicsDevice; 33 import java.awt.GraphicsEnvironment; 34 import java.awt.Insets; 35 import java.awt.Point; 36 import java.awt.Rectangle; 37 import java.awt.Toolkit; 38 import java.awt.event.*; 39 import java.beans.JavaBean; 40 import java.beans.BeanProperty; 41 import java.beans.PropertyChangeListener; 42 43 import java.util.*; 44 45 import java.io.Serializable; 46 import java.io.ObjectOutputStream; 47 import java.io.IOException; 48 49 import javax.swing.event.*; 50 import javax.swing.plaf.*; 51 import javax.accessibility.*; 52 53 /** 54 * An implementation of a menu -- a popup window containing 55 * <code>JMenuItem</code>s that 56 * is displayed when the user selects an item on the <code>JMenuBar</code>. 57 * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can 58 * also contain <code>JSeparator</code>s. 59 * <p> 60 * In essence, a menu is a button with an associated <code>JPopupMenu</code>. 61 * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the 62 * "button" is on the <code>JMenuBar</code>, the menu is a top-level window. 63 * If the "button" is another menu item, then the <code>JPopupMenu</code> is 64 * "pull-right" menu. 65 * <p> 66 * Menus can be configured, and to some degree controlled, by 67 * <code><a href="Action.html">Action</a></code>s. Using an 68 * <code>Action</code> with a menu has many benefits beyond directly 69 * configuring a menu. Refer to <a href="Action.html#buttonActions"> 70 * Swing Components Supporting <code>Action</code></a> for more 71 * details, and you can find more information in <a 72 * href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How 73 * to Use Actions</a>, a section in <em>The Java Tutorial</em>. 74 * <p> 75 * For information and examples of using menus see 76 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>, 77 * a section in <em>The Java Tutorial.</em> 78 * <p> 79 * <strong>Warning:</strong> Swing is not thread safe. For more 80 * information see <a 81 * href="package-summary.html#threading">Swing's Threading 82 * Policy</a>. 83 * <p> 84 * <strong>Warning:</strong> 85 * Serialized objects of this class will not be compatible with 86 * future Swing releases. The current serialization support is 87 * appropriate for short term storage or RMI between applications running 88 * the same version of Swing. As of 1.4, support for long term storage 89 * of all JavaBeans™ 90 * has been added to the <code>java.beans</code> package. 91 * Please see {@link java.beans.XMLEncoder}. 92 * 93 * @author Georges Saab 94 * @author David Karlton 95 * @author Arnaud Weber 96 * @see JMenuItem 97 * @see JSeparator 98 * @see JMenuBar 99 * @see JPopupMenu 100 * @since 1.2 101 */ 102 @JavaBean(description = "A popup window containing menu items displayed in a menu bar.") 103 @SwingContainer 104 @SuppressWarnings("serial") 105 public class JMenu extends JMenuItem implements Accessible,MenuElement 106 { 107 /** 108 * @see #getUIClassID 109 * @see #readObject 110 */ 111 private static final String uiClassID = "MenuUI"; 112 113 /* 114 * The popup menu portion of the menu. 115 */ 116 private JPopupMenu popupMenu; 117 118 /* 119 * The button's model listeners. Default is <code>null</code>. 120 */ 121 private ChangeListener menuChangeListener = null; 122 123 /* 124 * Only one <code>MenuEvent</code> is needed for each menu since the 125 * event's only state is the source property. The source of events 126 * generated is always "this". Default is <code>null</code>. 127 */ 128 private MenuEvent menuEvent = null; 129 130 /* 131 * Used by the look and feel (L&F) code to handle 132 * implementation specific menu behaviors. 133 */ 134 private int delay; 135 136 /* 137 * Location of the popup component. Location is <code>null</code> 138 * if it was not customized by <code>setMenuLocation</code> 139 */ 140 private Point customMenuLocation = null; 141 142 /* Diagnostic aids -- should be false for production builds. */ 143 private static final boolean TRACE = false; // trace creates and disposes 144 private static final boolean VERBOSE = false; // show reuse hits/misses 145 private static final boolean DEBUG = false; // show bad params, misc. 146 147 /** 148 * Constructs a new <code>JMenu</code> with no text. 149 */ 150 public JMenu() { 151 this(""); 152 } 153 154 /** 155 * Constructs a new <code>JMenu</code> with the supplied string 156 * as its text. 157 * 158 * @param s the text for the menu label 159 */ 160 public JMenu(String s) { 161 super(s); 162 } 163 164 /** 165 * Constructs a menu whose properties are taken from the 166 * <code>Action</code> supplied. 167 * @param a an <code>Action</code> 168 * 169 * @since 1.3 170 */ 171 public JMenu(Action a) { 172 this(); 173 setAction(a); 174 } 175 176 /** 177 * Constructs a new <code>JMenu</code> with the supplied string as 178 * its text and specified as a tear-off menu or not. 179 * 180 * @param s the text for the menu label 181 * @param b can the menu be torn off (not yet implemented) 182 */ 183 public JMenu(String s, boolean b) { 184 this(s); 185 } 186 187 188 /** 189 * Overriden to do nothing. We want JMenu to be focusable, but 190 * <code>JMenuItem</code> doesn't want to be, thus we override this 191 * do nothing. We don't invoke <code>setFocusable(true)</code> after 192 * super's constructor has completed as this has the side effect that 193 * <code>JMenu</code> will be considered traversable via the 194 * keyboard, which we don't want. Making a Component traversable by 195 * the keyboard after invoking <code>setFocusable(true)</code> is OK, 196 * as <code>setFocusable</code> is new API 197 * and is speced as such, but internally we don't want to use it like 198 * this else we change the keyboard traversability. 199 */ 200 void initFocusability() { 201 } 202 203 /** 204 * Resets the UI property with a value from the current look and feel. 205 * 206 * @see JComponent#updateUI 207 */ 208 public void updateUI() { 209 setUI((MenuItemUI)UIManager.getUI(this)); 210 211 if ( popupMenu != null ) 212 { 213 popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu)); 214 } 215 216 } 217 218 219 /** 220 * Returns the name of the L&F class that renders this component. 221 * 222 * @return the string "MenuUI" 223 * @see JComponent#getUIClassID 224 * @see UIDefaults#getUI 225 */ 226 @BeanProperty(bound = false) 227 public String getUIClassID() { 228 return uiClassID; 229 } 230 231 // public void repaint(long tm, int x, int y, int width, int height) { 232 // Thread.currentThread().dumpStack(); 233 // super.repaint(tm,x,y,width,height); 234 // } 235 236 /** 237 * Sets the data model for the "menu button" -- the label 238 * that the user clicks to open or close the menu. 239 * 240 * @param newModel the <code>ButtonModel</code> 241 * @see #getModel 242 */ 243 public void setModel(ButtonModel newModel) { 244 ButtonModel oldModel = getModel(); 245 246 super.setModel(newModel); 247 248 if (oldModel != null && menuChangeListener != null) { 249 oldModel.removeChangeListener(menuChangeListener); 250 menuChangeListener = null; 251 } 252 253 model = newModel; 254 255 if (newModel != null) { 256 menuChangeListener = createMenuChangeListener(); 257 newModel.addChangeListener(menuChangeListener); 258 } 259 } 260 261 /** 262 * Returns true if the menu is currently selected (highlighted). 263 * 264 * @return true if the menu is selected, else false 265 */ 266 public boolean isSelected() { 267 return getModel().isSelected(); 268 } 269 270 /** 271 * Sets the selection status of the menu. 272 * 273 * @param b true to select (highlight) the menu; false to de-select 274 * the menu 275 */ 276 @BeanProperty(expert = true, hidden = true, description 277 = "When the menu is selected, its popup child is shown.") 278 public void setSelected(boolean b) { 279 ButtonModel model = getModel(); 280 boolean oldValue = model.isSelected(); 281 282 // TIGER - 4840653 283 // Removed code which fired an AccessibleState.SELECTED 284 // PropertyChangeEvent since this resulted in two 285 // identical events being fired since 286 // AbstractButton.fireItemStateChanged also fires the 287 // same event. This caused screen readers to speak the 288 // name of the item twice. 289 290 if (b != model.isSelected()) { 291 getModel().setSelected(b); 292 } 293 } 294 295 /** 296 * Returns true if the menu's popup window is visible. 297 * 298 * @return true if the menu is visible, else false 299 */ 300 public boolean isPopupMenuVisible() { 301 ensurePopupMenuCreated(); 302 return popupMenu.isVisible(); 303 } 304 305 /** 306 * Sets the visibility of the menu's popup. If the menu is 307 * not enabled, this method will have no effect. 308 * 309 * @param b a boolean value -- true to make the menu visible, 310 * false to hide it 311 */ 312 @BeanProperty(bound = false, expert = true, hidden = true, description 313 = "The popup menu's visibility") 314 public void setPopupMenuVisible(boolean b) { 315 if (DEBUG) { 316 System.out.println("in JMenu.setPopupMenuVisible " + b); 317 // Thread.dumpStack(); 318 } 319 320 boolean isVisible = isPopupMenuVisible(); 321 if (b != isVisible && (isEnabled() || !b)) { 322 ensurePopupMenuCreated(); 323 if ((b==true) && isShowing()) { 324 // Set location of popupMenu (pulldown or pullright) 325 Point p = getCustomMenuLocation(); 326 if (p == null) { 327 p = getPopupMenuOrigin(); 328 } 329 getPopupMenu().show(this, p.x, p.y); 330 } else { 331 getPopupMenu().setVisible(false); 332 } 333 } 334 } 335 336 /** 337 * Computes the origin for the <code>JMenu</code>'s popup menu. 338 * This method uses Look and Feel properties named 339 * <code>Menu.menuPopupOffsetX</code>, 340 * <code>Menu.menuPopupOffsetY</code>, 341 * <code>Menu.submenuPopupOffsetX</code>, and 342 * <code>Menu.submenuPopupOffsetY</code> 343 * to adjust the exact location of popup. 344 * 345 * @return a <code>Point</code> in the coordinate space of the 346 * menu which should be used as the origin 347 * of the <code>JMenu</code>'s popup menu 348 * 349 * @since 1.3 350 */ 351 protected Point getPopupMenuOrigin() { 352 int x; 353 int y; 354 JPopupMenu pm = getPopupMenu(); 355 // Figure out the sizes needed to caclulate the menu position 356 Dimension s = getSize(); 357 Dimension pmSize = pm.getSize(); 358 // For the first time the menu is popped up, 359 // the size has not yet been initiated 360 if (pmSize.width==0) { 361 pmSize = pm.getPreferredSize(); 362 } 363 Point position = getLocationOnScreen(); 364 Toolkit toolkit = Toolkit.getDefaultToolkit(); 365 GraphicsConfiguration gc = getGraphicsConfiguration(); 366 Rectangle screenBounds = new Rectangle(toolkit.getScreenSize()); 367 GraphicsEnvironment ge = 368 GraphicsEnvironment.getLocalGraphicsEnvironment(); 369 GraphicsDevice[] gd = ge.getScreenDevices(); 370 for(int i = 0; i < gd.length; i++) { 371 if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) { 372 GraphicsConfiguration dgc = 373 gd[i].getDefaultConfiguration(); 374 if(dgc.getBounds().contains(position)) { 375 gc = dgc; 376 break; 377 } 378 } 379 } 380 381 382 if (gc != null) { 383 screenBounds = gc.getBounds(); 384 // take screen insets (e.g. taskbar) into account 385 Insets screenInsets = toolkit.getScreenInsets(gc); 386 387 screenBounds.width -= 388 Math.abs(screenInsets.left + screenInsets.right); 389 screenBounds.height -= 390 Math.abs(screenInsets.top + screenInsets.bottom); 391 position.x -= Math.abs(screenInsets.left); 392 position.y -= Math.abs(screenInsets.top); 393 } 394 395 Container parent = getParent(); 396 if (parent instanceof JPopupMenu) { 397 // We are a submenu (pull-right) 398 int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX"); 399 int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY"); 400 401 if( SwingUtilities.isLeftToRight(this) ) { 402 // First determine x: 403 x = s.width + xOffset; // Prefer placement to the right 404 if (position.x + x + pmSize.width >= screenBounds.width 405 + screenBounds.x && 406 // popup doesn't fit - place it wherever there's more room 407 screenBounds.width - s.width < 2*(position.x 408 - screenBounds.x)) { 409 410 x = 0 - xOffset - pmSize.width; 411 } 412 } else { 413 // First determine x: 414 x = 0 - xOffset - pmSize.width; // Prefer placement to the left 415 if (position.x + x < screenBounds.x && 416 // popup doesn't fit - place it wherever there's more room 417 screenBounds.width - s.width > 2*(position.x - 418 screenBounds.x)) { 419 420 x = s.width + xOffset; 421 } 422 } 423 // Then the y: 424 y = yOffset; // Prefer dropping down 425 if (position.y + y + pmSize.height >= screenBounds.height 426 + screenBounds.y && 427 // popup doesn't fit - place it wherever there's more room 428 screenBounds.height - s.height < 2*(position.y 429 - screenBounds.y)) { 430 431 y = s.height - yOffset - pmSize.height; 432 } 433 } else { 434 // We are a toplevel menu (pull-down) 435 int xOffset = UIManager.getInt("Menu.menuPopupOffsetX"); 436 int yOffset = UIManager.getInt("Menu.menuPopupOffsetY"); 437 438 if( SwingUtilities.isLeftToRight(this) ) { 439 // First determine the x: 440 x = xOffset; // Extend to the right 441 if (position.x + x + pmSize.width >= screenBounds.width 442 + screenBounds.x && 443 // popup doesn't fit - place it wherever there's more room 444 screenBounds.width - s.width < 2*(position.x 445 - screenBounds.x)) { 446 447 x = s.width - xOffset - pmSize.width; 448 } 449 } else { 450 // First determine the x: 451 x = s.width - xOffset - pmSize.width; // Extend to the left 452 if (position.x + x < screenBounds.x && 453 // popup doesn't fit - place it wherever there's more room 454 screenBounds.width - s.width > 2*(position.x 455 - screenBounds.x)) { 456 457 x = xOffset; 458 } 459 } 460 // Then the y: 461 y = s.height + yOffset; // Prefer dropping down 462 if (position.y + y + pmSize.height >= screenBounds.height && 463 // popup doesn't fit - place it wherever there's more room 464 screenBounds.height - s.height < 2*(position.y 465 - screenBounds.y)) { 466 467 y = 0 - yOffset - pmSize.height; // Otherwise drop 'up' 468 } 469 } 470 return new Point(x,y); 471 } 472 473 474 /** 475 * Returns the suggested delay, in milliseconds, before submenus 476 * are popped up or down. 477 * Each look and feel (L&F) may determine its own policy for 478 * observing the <code>delay</code> property. 479 * In most cases, the delay is not observed for top level menus 480 * or while dragging. The default for <code>delay</code> is 0. 481 * This method is a property of the look and feel code and is used 482 * to manage the idiosyncrasies of the various UI implementations. 483 * 484 * 485 * @return the <code>delay</code> property 486 */ 487 public int getDelay() { 488 return delay; 489 } 490 491 /** 492 * Sets the suggested delay before the menu's <code>PopupMenu</code> 493 * is popped up or down. Each look and feel (L&F) may determine 494 * it's own policy for observing the delay property. In most cases, 495 * the delay is not observed for top level menus or while dragging. 496 * This method is a property of the look and feel code and is used 497 * to manage the idiosyncrasies of the various UI implementations. 498 * 499 * @param d the number of milliseconds to delay 500 * @exception IllegalArgumentException if <code>d</code> 501 * is less than 0 502 */ 503 @BeanProperty(bound = false, expert = true, description 504 = "The delay between menu selection and making the popup menu visible") 505 public void setDelay(int d) { 506 if (d < 0) 507 throw new IllegalArgumentException("Delay must be a positive integer"); 508 509 delay = d; 510 } 511 512 /** 513 * The window-closing listener for the popup. 514 * 515 * @see WinListener 516 */ 517 protected WinListener popupListener; 518 519 private void ensurePopupMenuCreated() { 520 if (popupMenu == null) { 521 final JMenu thisMenu = this; 522 this.popupMenu = new JPopupMenu(); 523 popupMenu.setInvoker(this); 524 popupListener = createWinListener(popupMenu); 525 } 526 } 527 528 /* 529 * Return the customized location of the popup component. 530 */ 531 private Point getCustomMenuLocation() { 532 return customMenuLocation; 533 } 534 535 /** 536 * Sets the location of the popup component. 537 * 538 * @param x the x coordinate of the popup's new position 539 * @param y the y coordinate of the popup's new position 540 */ 541 public void setMenuLocation(int x, int y) { 542 customMenuLocation = new Point(x, y); 543 if (popupMenu != null) 544 popupMenu.setLocation(x, y); 545 } 546 547 /** 548 * Appends a menu item to the end of this menu. 549 * Returns the menu item added. 550 * 551 * @param menuItem the <code>JMenuitem</code> to be added 552 * @return the <code>JMenuItem</code> added 553 */ 554 public JMenuItem add(JMenuItem menuItem) { 555 ensurePopupMenuCreated(); 556 return popupMenu.add(menuItem); 557 } 558 559 /** 560 * Appends a component to the end of this menu. 561 * Returns the component added. 562 * 563 * @param c the <code>Component</code> to add 564 * @return the <code>Component</code> added 565 */ 566 public Component add(Component c) { 567 ensurePopupMenuCreated(); 568 popupMenu.add(c); 569 return c; 570 } 571 572 /** 573 * Adds the specified component to this container at the given 574 * position. If <code>index</code> equals -1, the component will 575 * be appended to the end. 576 * @param c the <code>Component</code> to add 577 * @param index the position at which to insert the component 578 * @return the <code>Component</code> added 579 * @see #remove 580 * @see java.awt.Container#add(Component, int) 581 */ 582 public Component add(Component c, int index) { 583 ensurePopupMenuCreated(); 584 popupMenu.add(c, index); 585 return c; 586 } 587 588 /** 589 * Creates a new menu item with the specified text and appends 590 * it to the end of this menu. 591 * 592 * @param s the string for the menu item to be added 593 * @return the new {@code JMenuItem} 594 */ 595 public JMenuItem add(String s) { 596 return add(new JMenuItem(s)); 597 } 598 599 /** 600 * Creates a new menu item attached to the specified {@code Action} object 601 * and appends it to the end of this menu. 602 * 603 * @param a the {@code Action} for the menu item to be added 604 * @return the new {@code JMenuItem} 605 * @see Action 606 */ 607 public JMenuItem add(Action a) { 608 JMenuItem mi = createActionComponent(a); 609 mi.setAction(a); 610 add(mi); 611 return mi; 612 } 613 614 /** 615 * Factory method which creates the <code>JMenuItem</code> for 616 * <code>Action</code>s added to the <code>JMenu</code>. 617 * 618 * @param a the <code>Action</code> for the menu item to be added 619 * @return the new menu item 620 * @see Action 621 * 622 * @since 1.3 623 */ 624 protected JMenuItem createActionComponent(Action a) { 625 JMenuItem mi = new JMenuItem() { 626 protected PropertyChangeListener createActionPropertyChangeListener(Action a) { 627 PropertyChangeListener pcl = createActionChangeListener(this); 628 if (pcl == null) { 629 pcl = super.createActionPropertyChangeListener(a); 630 } 631 return pcl; 632 } 633 }; 634 mi.setHorizontalTextPosition(JButton.TRAILING); 635 mi.setVerticalTextPosition(JButton.CENTER); 636 return mi; 637 } 638 639 /** 640 * Returns a properly configured {@code PropertyChangeListener} 641 * which updates the control as changes to the {@code Action} occur. 642 * 643 * @param b a menu item for which to create a {@code PropertyChangeListener} 644 * @return a {@code PropertyChangeListener} for {@code b} 645 */ 646 protected PropertyChangeListener createActionChangeListener(JMenuItem b) { 647 return b.createActionPropertyChangeListener0(b.getAction()); 648 } 649 650 /** 651 * Appends a new separator to the end of the menu. 652 */ 653 public void addSeparator() 654 { 655 ensurePopupMenuCreated(); 656 popupMenu.addSeparator(); 657 } 658 659 /** 660 * Inserts a new menu item with the specified text at a 661 * given position. 662 * 663 * @param s the text for the menu item to add 664 * @param pos an integer specifying the position at which to add the 665 * new menu item 666 * @exception IllegalArgumentException when the value of 667 * <code>pos</code> < 0 668 */ 669 public void insert(String s, int pos) { 670 if (pos < 0) { 671 throw new IllegalArgumentException("index less than zero."); 672 } 673 674 ensurePopupMenuCreated(); 675 popupMenu.insert(new JMenuItem(s), pos); 676 } 677 678 /** 679 * Inserts the specified <code>JMenuitem</code> at a given position. 680 * 681 * @param mi the <code>JMenuitem</code> to add 682 * @param pos an integer specifying the position at which to add the 683 * new <code>JMenuitem</code> 684 * @return the new menu item 685 * @exception IllegalArgumentException if the value of 686 * <code>pos</code> < 0 687 */ 688 public JMenuItem insert(JMenuItem mi, int pos) { 689 if (pos < 0) { 690 throw new IllegalArgumentException("index less than zero."); 691 } 692 ensurePopupMenuCreated(); 693 popupMenu.insert(mi, pos); 694 return mi; 695 } 696 697 /** 698 * Inserts a new menu item attached to the specified <code>Action</code> 699 * object at a given position. 700 * 701 * @param a the <code>Action</code> object for the menu item to add 702 * @param pos an integer specifying the position at which to add the 703 * new menu item 704 * @return the new menu item 705 * @exception IllegalArgumentException if the value of 706 * <code>pos</code> < 0 707 */ 708 public JMenuItem insert(Action a, int pos) { 709 if (pos < 0) { 710 throw new IllegalArgumentException("index less than zero."); 711 } 712 713 ensurePopupMenuCreated(); 714 JMenuItem mi = new JMenuItem(a); 715 mi.setHorizontalTextPosition(JButton.TRAILING); 716 mi.setVerticalTextPosition(JButton.CENTER); 717 popupMenu.insert(mi, pos); 718 return mi; 719 } 720 721 /** 722 * Inserts a separator at the specified position. 723 * 724 * @param index an integer specifying the position at which to 725 * insert the menu separator 726 * @exception IllegalArgumentException if the value of 727 * <code>index</code> < 0 728 */ 729 public void insertSeparator(int index) { 730 if (index < 0) { 731 throw new IllegalArgumentException("index less than zero."); 732 } 733 734 ensurePopupMenuCreated(); 735 popupMenu.insert( new JPopupMenu.Separator(), index ); 736 } 737 738 /** 739 * Returns the {@code JMenuItem} at the specified position. 740 * If the component at {@code pos} is not a menu item, 741 * {@code null} is returned. 742 * This method is included for AWT compatibility. 743 * 744 * @param pos an integer specifying the position 745 * @return the menu item at the specified position; or <code>null</code> 746 * if the item as the specified position is not a menu item 747 * @exception IllegalArgumentException if the value of 748 * {@code pos} < 0 749 */ 750 public JMenuItem getItem(int pos) { 751 if (pos < 0) { 752 throw new IllegalArgumentException("index less than zero."); 753 } 754 755 Component c = getMenuComponent(pos); 756 if (c instanceof JMenuItem) { 757 JMenuItem mi = (JMenuItem) c; 758 return mi; 759 } 760 761 // 4173633 762 return null; 763 } 764 765 /** 766 * Returns the number of items on the menu, including separators. 767 * This method is included for AWT compatibility. 768 * 769 * @return an integer equal to the number of items on the menu 770 * @see #getMenuComponentCount 771 */ 772 @BeanProperty(bound = false) 773 public int getItemCount() { 774 return getMenuComponentCount(); 775 } 776 777 /** 778 * Returns true if the menu can be torn off. This method is not 779 * yet implemented. 780 * 781 * @return true if the menu can be torn off, else false 782 * @exception Error if invoked -- this method is not yet implemented 783 */ 784 @BeanProperty(bound = false) 785 public boolean isTearOff() { 786 throw new Error("boolean isTearOff() {} not yet implemented"); 787 } 788 789 /** 790 * Removes the specified menu item from this menu. If there is no 791 * popup menu, this method will have no effect. 792 * 793 * @param item the <code>JMenuItem</code> to be removed from the menu 794 */ 795 public void remove(JMenuItem item) { 796 if (popupMenu != null) 797 popupMenu.remove(item); 798 } 799 800 /** 801 * Removes the menu item at the specified index from this menu. 802 * 803 * @param pos the position of the item to be removed 804 * @exception IllegalArgumentException if the value of 805 * <code>pos</code> < 0, or if <code>pos</code> 806 * is greater than the number of menu items 807 */ 808 public void remove(int pos) { 809 if (pos < 0) { 810 throw new IllegalArgumentException("index less than zero."); 811 } 812 if (pos > getItemCount()) { 813 throw new IllegalArgumentException("index greater than the number of items."); 814 } 815 if (popupMenu != null) 816 popupMenu.remove(pos); 817 } 818 819 /** 820 * Removes the component <code>c</code> from this menu. 821 * 822 * @param c the component to be removed 823 */ 824 public void remove(Component c) { 825 if (popupMenu != null) 826 popupMenu.remove(c); 827 } 828 829 /** 830 * Removes all menu items from this menu. 831 */ 832 public void removeAll() { 833 if (popupMenu != null) 834 popupMenu.removeAll(); 835 } 836 837 /** 838 * Returns the number of components on the menu. 839 * 840 * @return an integer containing the number of components on the menu 841 */ 842 @BeanProperty(bound = false) 843 public int getMenuComponentCount() { 844 int componentCount = 0; 845 if (popupMenu != null) 846 componentCount = popupMenu.getComponentCount(); 847 return componentCount; 848 } 849 850 /** 851 * Returns the component at position <code>n</code>. 852 * 853 * @param n the position of the component to be returned 854 * @return the component requested, or <code>null</code> 855 * if there is no popup menu 856 * 857 */ 858 public Component getMenuComponent(int n) { 859 if (popupMenu != null) 860 return popupMenu.getComponent(n); 861 862 return null; 863 } 864 865 /** 866 * Returns an array of <code>Component</code>s of the menu's 867 * subcomponents. Note that this returns all <code>Component</code>s 868 * in the popup menu, including separators. 869 * 870 * @return an array of <code>Component</code>s or an empty array 871 * if there is no popup menu 872 */ 873 @BeanProperty(bound = false) 874 public Component[] getMenuComponents() { 875 if (popupMenu != null) 876 return popupMenu.getComponents(); 877 878 return new Component[0]; 879 } 880 881 /** 882 * Returns true if the menu is a 'top-level menu', that is, if it is 883 * the direct child of a menubar. 884 * 885 * @return true if the menu is activated from the menu bar; 886 * false if the menu is activated from a menu item 887 * on another menu 888 */ 889 @BeanProperty(bound = false) 890 public boolean isTopLevelMenu() { 891 return getParent() instanceof JMenuBar; 892 893 } 894 895 /** 896 * Returns true if the specified component exists in the 897 * submenu hierarchy. 898 * 899 * @param c the <code>Component</code> to be tested 900 * @return true if the <code>Component</code> exists, false otherwise 901 */ 902 public boolean isMenuComponent(Component c) { 903 // Are we in the MenuItem part of the menu 904 if (c == this) 905 return true; 906 // Are we in the PopupMenu? 907 if (c instanceof JPopupMenu) { 908 JPopupMenu comp = (JPopupMenu) c; 909 if (comp == this.getPopupMenu()) 910 return true; 911 } 912 // Are we in a Component on the PopupMenu 913 int ncomponents = this.getMenuComponentCount(); 914 Component[] component = this.getMenuComponents(); 915 for (int i = 0 ; i < ncomponents ; i++) { 916 Component comp = component[i]; 917 // Are we in the current component? 918 if (comp == c) 919 return true; 920 // Hmmm, what about Non-menu containers? 921 922 // Recursive call for the Menu case 923 if (comp instanceof JMenu) { 924 JMenu subMenu = (JMenu) comp; 925 if (subMenu.isMenuComponent(c)) 926 return true; 927 } 928 } 929 return false; 930 } 931 932 933 /* 934 * Returns a point in the coordinate space of this menu's popupmenu 935 * which corresponds to the point <code>p</code> in the menu's 936 * coordinate space. 937 * 938 * @param p the point to be translated 939 * @return the point in the coordinate space of this menu's popupmenu 940 */ 941 private Point translateToPopupMenu(Point p) { 942 return translateToPopupMenu(p.x, p.y); 943 } 944 945 /* 946 * Returns a point in the coordinate space of this menu's popupmenu 947 * which corresponds to the point (x,y) in the menu's coordinate space. 948 * 949 * @param x the x coordinate of the point to be translated 950 * @param y the y coordinate of the point to be translated 951 * @return the point in the coordinate space of this menu's popupmenu 952 */ 953 private Point translateToPopupMenu(int x, int y) { 954 int newX; 955 int newY; 956 957 if (getParent() instanceof JPopupMenu) { 958 newX = x - getSize().width; 959 newY = y; 960 } else { 961 newX = x; 962 newY = y - getSize().height; 963 } 964 965 return new Point(newX, newY); 966 } 967 968 /** 969 * Returns the popupmenu associated with this menu. If there is 970 * no popupmenu, it will create one. 971 * 972 * @return the {@code JPopupMenu} associated with this menu 973 */ 974 @BeanProperty(bound = false) 975 public JPopupMenu getPopupMenu() { 976 ensurePopupMenuCreated(); 977 return popupMenu; 978 } 979 980 /** 981 * Adds a listener for menu events. 982 * 983 * @param l the listener to be added 984 */ 985 public void addMenuListener(MenuListener l) { 986 listenerList.add(MenuListener.class, l); 987 } 988 989 /** 990 * Removes a listener for menu events. 991 * 992 * @param l the listener to be removed 993 */ 994 public void removeMenuListener(MenuListener l) { 995 listenerList.remove(MenuListener.class, l); 996 } 997 998 /** 999 * Returns an array of all the <code>MenuListener</code>s added 1000 * to this JMenu with addMenuListener(). 1001 * 1002 * @return all of the <code>MenuListener</code>s added or an empty 1003 * array if no listeners have been added 1004 * @since 1.4 1005 */ 1006 @BeanProperty(bound = false) 1007 public MenuListener[] getMenuListeners() { 1008 return listenerList.getListeners(MenuListener.class); 1009 } 1010 1011 /** 1012 * Notifies all listeners that have registered interest for 1013 * notification on this event type. The event instance 1014 * is created lazily. 1015 * 1016 * @exception Error if there is a <code>null</code> listener 1017 * @see EventListenerList 1018 */ 1019 protected void fireMenuSelected() { 1020 if (DEBUG) { 1021 System.out.println("In JMenu.fireMenuSelected"); 1022 } 1023 // Guaranteed to return a non-null array 1024 Object[] listeners = listenerList.getListenerList(); 1025 // Process the listeners last to first, notifying 1026 // those that are interested in this event 1027 for (int i = listeners.length-2; i>=0; i-=2) { 1028 if (listeners[i]==MenuListener.class) { 1029 if (listeners[i+1]== null) { 1030 throw new Error(getText() +" has a NULL Listener!! " + i); 1031 } else { 1032 // Lazily create the event: 1033 if (menuEvent == null) 1034 menuEvent = new MenuEvent(this); 1035 ((MenuListener)listeners[i+1]).menuSelected(menuEvent); 1036 } 1037 } 1038 } 1039 } 1040 1041 /** 1042 * Notifies all listeners that have registered interest for 1043 * notification on this event type. The event instance 1044 * is created lazily. 1045 * 1046 * @exception Error if there is a <code>null</code> listener 1047 * @see EventListenerList 1048 */ 1049 protected void fireMenuDeselected() { 1050 if (DEBUG) { 1051 System.out.println("In JMenu.fireMenuDeselected"); 1052 } 1053 // Guaranteed to return a non-null array 1054 Object[] listeners = listenerList.getListenerList(); 1055 // Process the listeners last to first, notifying 1056 // those that are interested in this event 1057 for (int i = listeners.length-2; i>=0; i-=2) { 1058 if (listeners[i]==MenuListener.class) { 1059 if (listeners[i+1]== null) { 1060 throw new Error(getText() +" has a NULL Listener!! " + i); 1061 } else { 1062 // Lazily create the event: 1063 if (menuEvent == null) 1064 menuEvent = new MenuEvent(this); 1065 ((MenuListener)listeners[i+1]).menuDeselected(menuEvent); 1066 } 1067 } 1068 } 1069 } 1070 1071 /** 1072 * Notifies all listeners that have registered interest for 1073 * notification on this event type. The event instance 1074 * is created lazily. 1075 * 1076 * @exception Error if there is a <code>null</code> listener 1077 * @see EventListenerList 1078 */ 1079 protected void fireMenuCanceled() { 1080 if (DEBUG) { 1081 System.out.println("In JMenu.fireMenuCanceled"); 1082 } 1083 // Guaranteed to return a non-null array 1084 Object[] listeners = listenerList.getListenerList(); 1085 // Process the listeners last to first, notifying 1086 // those that are interested in this event 1087 for (int i = listeners.length-2; i>=0; i-=2) { 1088 if (listeners[i]==MenuListener.class) { 1089 if (listeners[i+1]== null) { 1090 throw new Error(getText() +" has a NULL Listener!! " 1091 + i); 1092 } else { 1093 // Lazily create the event: 1094 if (menuEvent == null) 1095 menuEvent = new MenuEvent(this); 1096 ((MenuListener)listeners[i+1]).menuCanceled(menuEvent); 1097 } 1098 } 1099 } 1100 } 1101 1102 // Overriden to do nothing, JMenu doesn't support an accelerator 1103 void configureAcceleratorFromAction(Action a) { 1104 } 1105 1106 @SuppressWarnings("serial") 1107 class MenuChangeListener implements ChangeListener, Serializable { 1108 boolean isSelected = false; 1109 public void stateChanged(ChangeEvent e) { 1110 ButtonModel model = (ButtonModel) e.getSource(); 1111 boolean modelSelected = model.isSelected(); 1112 1113 if (modelSelected != isSelected) { 1114 if (modelSelected == true) { 1115 fireMenuSelected(); 1116 } else { 1117 fireMenuDeselected(); 1118 } 1119 isSelected = modelSelected; 1120 } 1121 } 1122 } 1123 1124 private ChangeListener createMenuChangeListener() { 1125 return new MenuChangeListener(); 1126 } 1127 1128 1129 /** 1130 * Creates a window-closing listener for the popup. 1131 * 1132 * @param p the <code>JPopupMenu</code> 1133 * @return the new window-closing listener 1134 * 1135 * @see WinListener 1136 */ 1137 protected WinListener createWinListener(JPopupMenu p) { 1138 return new WinListener(p); 1139 } 1140 1141 /** 1142 * A listener class that watches for a popup window closing. 1143 * When the popup is closing, the listener deselects the menu. 1144 * <p> 1145 * <strong>Warning:</strong> 1146 * Serialized objects of this class will not be compatible with 1147 * future Swing releases. The current serialization support is 1148 * appropriate for short term storage or RMI between applications running 1149 * the same version of Swing. As of 1.4, support for long term storage 1150 * of all JavaBeans™ 1151 * has been added to the <code>java.beans</code> package. 1152 * Please see {@link java.beans.XMLEncoder}. 1153 */ 1154 @SuppressWarnings("serial") 1155 protected class WinListener extends WindowAdapter implements Serializable { 1156 JPopupMenu popupMenu; 1157 /** 1158 * Create the window listener for the specified popup. 1159 * 1160 * @param p the popup menu for which to create a listener 1161 * @since 1.4 1162 */ 1163 public WinListener(JPopupMenu p) { 1164 this.popupMenu = p; 1165 } 1166 /** 1167 * Deselect the menu when the popup is closed from outside. 1168 */ 1169 public void windowClosing(WindowEvent e) { 1170 setSelected(false); 1171 } 1172 } 1173 1174 /** 1175 * Messaged when the menubar selection changes to activate or 1176 * deactivate this menu. 1177 * Overrides <code>JMenuItem.menuSelectionChanged</code>. 1178 * 1179 * @param isIncluded true if this menu is active, false if 1180 * it is not 1181 */ 1182 public void menuSelectionChanged(boolean isIncluded) { 1183 if (DEBUG) { 1184 System.out.println("In JMenu.menuSelectionChanged to " + isIncluded); 1185 } 1186 setSelected(isIncluded); 1187 } 1188 1189 /** 1190 * Returns an array of <code>MenuElement</code>s containing the submenu 1191 * for this menu component. If popup menu is <code>null</code> returns 1192 * an empty array. This method is required to conform to the 1193 * <code>MenuElement</code> interface. Note that since 1194 * <code>JSeparator</code>s do not conform to the <code>MenuElement</code> 1195 * interface, this array will only contain <code>JMenuItem</code>s. 1196 * 1197 * @return an array of <code>MenuElement</code> objects 1198 */ 1199 @BeanProperty(bound = false) 1200 public MenuElement[] getSubElements() { 1201 if(popupMenu == null) 1202 return new MenuElement[0]; 1203 else { 1204 MenuElement result[] = new MenuElement[1]; 1205 result[0] = popupMenu; 1206 return result; 1207 } 1208 } 1209 1210 1211 // implements javax.swing.MenuElement 1212 /** 1213 * Returns the <code>java.awt.Component</code> used to 1214 * paint this <code>MenuElement</code>. 1215 * The returned component is used to convert events and detect if 1216 * an event is inside a menu component. 1217 */ 1218 public Component getComponent() { 1219 return this; 1220 } 1221 1222 1223 /** 1224 * Sets the <code>ComponentOrientation</code> property of this menu 1225 * and all components contained within it. This includes all 1226 * components returned by {@link #getMenuComponents getMenuComponents}. 1227 * 1228 * @param o the new component orientation of this menu and 1229 * the components contained within it. 1230 * @exception NullPointerException if <code>orientation</code> is null. 1231 * @see java.awt.Component#setComponentOrientation 1232 * @see java.awt.Component#getComponentOrientation 1233 * @since 1.4 1234 */ 1235 public void applyComponentOrientation(ComponentOrientation o) { 1236 super.applyComponentOrientation(o); 1237 1238 if ( popupMenu != null ) { 1239 int ncomponents = getMenuComponentCount(); 1240 for (int i = 0 ; i < ncomponents ; ++i) { 1241 getMenuComponent(i).applyComponentOrientation(o); 1242 } 1243 popupMenu.setComponentOrientation(o); 1244 } 1245 } 1246 1247 public void setComponentOrientation(ComponentOrientation o) { 1248 super.setComponentOrientation(o); 1249 if ( popupMenu != null ) { 1250 popupMenu.setComponentOrientation(o); 1251 } 1252 } 1253 1254 /** 1255 * <code>setAccelerator</code> is not defined for <code>JMenu</code>. 1256 * Use <code>setMnemonic</code> instead. 1257 * @param keyStroke the keystroke combination which will invoke 1258 * the <code>JMenuItem</code>'s actionlisteners 1259 * without navigating the menu hierarchy 1260 * @exception Error if invoked -- this method is not defined for JMenu. 1261 * Use <code>setMnemonic</code> instead 1262 */ 1263 public void setAccelerator(KeyStroke keyStroke) { 1264 throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead."); 1265 } 1266 1267 /** 1268 * Processes key stroke events such as mnemonics and accelerators. 1269 * 1270 * @param evt the key event to be processed 1271 */ 1272 protected void processKeyEvent(KeyEvent evt) { 1273 MenuSelectionManager.defaultManager().processKeyEvent(evt); 1274 if (evt.isConsumed()) 1275 return; 1276 1277 super.processKeyEvent(evt); 1278 } 1279 1280 /** 1281 * Programmatically performs a "click". This overrides the method 1282 * <code>AbstractButton.doClick</code> in order to make the menu pop up. 1283 * @param pressTime indicates the number of milliseconds the 1284 * button was pressed for 1285 */ 1286 public void doClick(int pressTime) { 1287 MenuElement me[] = buildMenuElementArray(this); 1288 MenuSelectionManager.defaultManager().setSelectedPath(me); 1289 } 1290 1291 /* 1292 * Build an array of menu elements - from <code>PopupMenu</code> to 1293 * the root <code>JMenuBar</code>. 1294 * @param leaf the leaf node from which to start building up the array 1295 * @return the array of menu items 1296 */ 1297 private MenuElement[] buildMenuElementArray(JMenu leaf) { 1298 Vector<MenuElement> elements = new Vector<MenuElement>(); 1299 Component current = leaf.getPopupMenu(); 1300 JPopupMenu pop; 1301 JMenu menu; 1302 JMenuBar bar; 1303 1304 while (true) { 1305 if (current instanceof JPopupMenu) { 1306 pop = (JPopupMenu) current; 1307 elements.insertElementAt(pop, 0); 1308 current = pop.getInvoker(); 1309 } else if (current instanceof JMenu) { 1310 menu = (JMenu) current; 1311 elements.insertElementAt(menu, 0); 1312 current = menu.getParent(); 1313 } else if (current instanceof JMenuBar) { 1314 bar = (JMenuBar) current; 1315 elements.insertElementAt(bar, 0); 1316 MenuElement me[] = new MenuElement[elements.size()]; 1317 elements.copyInto(me); 1318 return me; 1319 } 1320 } 1321 } 1322 1323 1324 /** 1325 * See <code>readObject</code> and <code>writeObject</code> in 1326 * <code>JComponent</code> for more 1327 * information about serialization in Swing. 1328 */ 1329 private void writeObject(ObjectOutputStream s) throws IOException { 1330 s.defaultWriteObject(); 1331 if (getUIClassID().equals(uiClassID)) { 1332 byte count = JComponent.getWriteObjCounter(this); 1333 JComponent.setWriteObjCounter(this, --count); 1334 if (count == 0 && ui != null) { 1335 ui.installUI(this); 1336 } 1337 } 1338 } 1339 1340 1341 /** 1342 * Returns a string representation of this <code>JMenu</code>. This 1343 * method is intended to be used only for debugging purposes, and the 1344 * content and format of the returned string may vary between 1345 * implementations. The returned string may be empty but may not 1346 * be <code>null</code>. 1347 * 1348 * @return a string representation of this JMenu. 1349 */ 1350 protected String paramString() { 1351 return super.paramString(); 1352 } 1353 1354 1355 ///////////////// 1356 // Accessibility support 1357 //////////////// 1358 1359 /** 1360 * Gets the AccessibleContext associated with this JMenu. 1361 * For JMenus, the AccessibleContext takes the form of an 1362 * AccessibleJMenu. 1363 * A new AccessibleJMenu instance is created if necessary. 1364 * 1365 * @return an AccessibleJMenu that serves as the 1366 * AccessibleContext of this JMenu 1367 */ 1368 @BeanProperty(bound = false) 1369 public AccessibleContext getAccessibleContext() { 1370 if (accessibleContext == null) { 1371 accessibleContext = new AccessibleJMenu(); 1372 } 1373 return accessibleContext; 1374 } 1375 1376 /** 1377 * This class implements accessibility support for the 1378 * <code>JMenu</code> class. It provides an implementation of the 1379 * Java Accessibility API appropriate to menu user-interface elements. 1380 * <p> 1381 * <strong>Warning:</strong> 1382 * Serialized objects of this class will not be compatible with 1383 * future Swing releases. The current serialization support is 1384 * appropriate for short term storage or RMI between applications running 1385 * the same version of Swing. As of 1.4, support for long term storage 1386 * of all JavaBeans™ 1387 * has been added to the <code>java.beans</code> package. 1388 * Please see {@link java.beans.XMLEncoder}. 1389 */ 1390 @SuppressWarnings("serial") 1391 protected class AccessibleJMenu extends AccessibleJMenuItem 1392 implements AccessibleSelection { 1393 1394 /** 1395 * Returns the number of accessible children in the object. If all 1396 * of the children of this object implement Accessible, than this 1397 * method should return the number of children of this object. 1398 * 1399 * @return the number of accessible children in the object. 1400 */ 1401 public int getAccessibleChildrenCount() { 1402 Component[] children = getMenuComponents(); 1403 int count = 0; 1404 for (Component child : children) { 1405 if (child instanceof Accessible) { 1406 count++; 1407 } 1408 } 1409 return count; 1410 } 1411 1412 /** 1413 * Returns the nth Accessible child of the object. 1414 * 1415 * @param i zero-based index of child 1416 * @return the nth Accessible child of the object 1417 */ 1418 public Accessible getAccessibleChild(int i) { 1419 Component[] children = getMenuComponents(); 1420 int count = 0; 1421 for (Component child : children) { 1422 if (child instanceof Accessible) { 1423 if (count == i) { 1424 if (child instanceof JComponent) { 1425 // FIXME: [[[WDW - probably should set this when 1426 // the component is added to the menu. I tried 1427 // to do this in most cases, but the separators 1428 // added by addSeparator are hard to get to.]]] 1429 AccessibleContext ac = child.getAccessibleContext(); 1430 ac.setAccessibleParent(JMenu.this); 1431 } 1432 return (Accessible) child; 1433 } else { 1434 count++; 1435 } 1436 } 1437 } 1438 return null; 1439 } 1440 1441 /** 1442 * Get the role of this object. 1443 * 1444 * @return an instance of AccessibleRole describing the role of the 1445 * object 1446 * @see AccessibleRole 1447 */ 1448 public AccessibleRole getAccessibleRole() { 1449 return AccessibleRole.MENU; 1450 } 1451 1452 /** 1453 * Get the AccessibleSelection associated with this object. In the 1454 * implementation of the Java Accessibility API for this class, 1455 * return this object, which is responsible for implementing the 1456 * AccessibleSelection interface on behalf of itself. 1457 * 1458 * @return this object 1459 */ 1460 public AccessibleSelection getAccessibleSelection() { 1461 return this; 1462 } 1463 1464 /** 1465 * Returns 1 if a sub-menu is currently selected in this menu. 1466 * 1467 * @return 1 if a menu is currently selected, else 0 1468 */ 1469 public int getAccessibleSelectionCount() { 1470 MenuElement me[] = 1471 MenuSelectionManager.defaultManager().getSelectedPath(); 1472 if (me != null) { 1473 for (int i = 0; i < me.length; i++) { 1474 if (me[i] == JMenu.this) { // this menu is selected 1475 if (i+1 < me.length) { 1476 return 1; 1477 } 1478 } 1479 } 1480 } 1481 return 0; 1482 } 1483 1484 /** 1485 * Returns the currently selected sub-menu if one is selected, 1486 * otherwise null (there can only be one selection, and it can 1487 * only be a sub-menu, as otherwise menu items don't remain 1488 * selected). 1489 */ 1490 public Accessible getAccessibleSelection(int i) { 1491 // if i is a sub-menu & popped, return it 1492 if (i < 0 || i >= getItemCount()) { 1493 return null; 1494 } 1495 MenuElement me[] = 1496 MenuSelectionManager.defaultManager().getSelectedPath(); 1497 if (me != null) { 1498 for (int j = 0; j < me.length; j++) { 1499 if (me[j] == JMenu.this) { // this menu is selected 1500 // so find the next JMenuItem in the MenuElement 1501 // array, and return it! 1502 while (++j < me.length) { 1503 if (me[j] instanceof JMenuItem) { 1504 return (Accessible) me[j]; 1505 } 1506 } 1507 } 1508 } 1509 } 1510 return null; 1511 } 1512 1513 /** 1514 * Returns true if the current child of this object is selected 1515 * (that is, if this child is a popped-up submenu). 1516 * 1517 * @param i the zero-based index of the child in this Accessible 1518 * object. 1519 * @see AccessibleContext#getAccessibleChild 1520 */ 1521 public boolean isAccessibleChildSelected(int i) { 1522 // if i is a sub-menu and is pop-ed up, return true, else false 1523 MenuElement me[] = 1524 MenuSelectionManager.defaultManager().getSelectedPath(); 1525 if (me != null) { 1526 JMenuItem mi = JMenu.this.getItem(i); 1527 for (int j = 0; j < me.length; j++) { 1528 if (me[j] == mi) { 1529 return true; 1530 } 1531 } 1532 } 1533 return false; 1534 } 1535 1536 1537 /** 1538 * Selects the <code>i</code>th menu in the menu. 1539 * If that item is a submenu, 1540 * it will pop up in response. If a different item is already 1541 * popped up, this will force it to close. If this is a sub-menu 1542 * that is already popped up (selected), this method has no 1543 * effect. 1544 * 1545 * @param i the index of the item to be selected 1546 * @see #getAccessibleStateSet 1547 */ 1548 public void addAccessibleSelection(int i) { 1549 if (i < 0 || i >= getItemCount()) { 1550 return; 1551 } 1552 JMenuItem mi = getItem(i); 1553 if (mi != null) { 1554 if (mi instanceof JMenu) { 1555 MenuElement me[] = buildMenuElementArray((JMenu) mi); 1556 MenuSelectionManager.defaultManager().setSelectedPath(me); 1557 } else { 1558 MenuSelectionManager.defaultManager().setSelectedPath(null); 1559 } 1560 } 1561 } 1562 1563 /** 1564 * Removes the nth item from the selection. In general, menus 1565 * can only have one item within them selected at a time 1566 * (e.g. one sub-menu popped open). 1567 * 1568 * @param i the zero-based index of the selected item 1569 */ 1570 public void removeAccessibleSelection(int i) { 1571 if (i < 0 || i >= getItemCount()) { 1572 return; 1573 } 1574 JMenuItem mi = getItem(i); 1575 if (mi != null && mi instanceof JMenu) { 1576 if (mi.isSelected()) { 1577 MenuElement old[] = 1578 MenuSelectionManager.defaultManager().getSelectedPath(); 1579 MenuElement me[] = new MenuElement[old.length-2]; 1580 for (int j = 0; j < old.length -2; j++) { 1581 me[j] = old[j]; 1582 } 1583 MenuSelectionManager.defaultManager().setSelectedPath(me); 1584 } 1585 } 1586 } 1587 1588 /** 1589 * Clears the selection in the object, so that nothing in the 1590 * object is selected. This will close any open sub-menu. 1591 */ 1592 public void clearAccessibleSelection() { 1593 // if this menu is selected, reset selection to only go 1594 // to this menu; else do nothing 1595 MenuElement old[] = 1596 MenuSelectionManager.defaultManager().getSelectedPath(); 1597 if (old != null) { 1598 for (int j = 0; j < old.length; j++) { 1599 if (old[j] == JMenu.this) { // menu is in the selection! 1600 MenuElement me[] = new MenuElement[j+1]; 1601 System.arraycopy(old, 0, me, 0, j); 1602 me[j] = JMenu.this.getPopupMenu(); 1603 MenuSelectionManager.defaultManager().setSelectedPath(me); 1604 } 1605 } 1606 } 1607 } 1608 1609 /** 1610 * Normally causes every selected item in the object to be selected 1611 * if the object supports multiple selections. This method 1612 * makes no sense in a menu bar, and so does nothing. 1613 */ 1614 public void selectAllAccessibleSelection() { 1615 } 1616 } // inner class AccessibleJMenu 1617 1618 }