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