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