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 // popup doesn't fit - place it wherever there's more room 481 screenBounds.height - s.height < 2*(position.y 482 - screenBounds.y)) { 483 484 y = 0 - yOffset - pmSize.height; // Otherwise drop 'up' 485 } 486 } 487 return new Point(x,y); 488 } 489 490 491 /** 492 * Returns the suggested delay, in milliseconds, before submenus 493 * are popped up or down. 494 * Each look and feel (L&F) may determine its own policy for 495 * observing the <code>delay</code> property. 496 * In most cases, the delay is not observed for top level menus 497 * or while dragging. The default for <code>delay</code> is 0. 498 * This method is a property of the look and feel code and is used 499 * to manage the idiosyncrasies of the various UI implementations. 500 * 501 * 502 * @return the <code>delay</code> property 503 */ 504 public int getDelay() { 505 return delay; 506 } 507 508 /** 509 * Sets the suggested delay before the menu's <code>PopupMenu</code> 510 * is popped up or down. Each look and feel (L&F) may determine 511 * it's own policy for observing the delay property. In most cases, 512 * the delay is not observed for top level menus or while dragging. 513 * This method is a property of the look and feel code and is used 514 * to manage the idiosyncrasies of the various UI implementations. 515 * 516 * @param d the number of milliseconds to delay 517 * @exception IllegalArgumentException if <code>d</code> 518 * is less than 0 519 * @beaninfo 520 * description: The delay between menu selection and making the popup menu visible 521 * expert: true 522 */ 523 public void setDelay(int d) { 524 if (d < 0) 525 throw new IllegalArgumentException("Delay must be a positive integer"); 526 527 delay = d; 528 } 529 530 /** 531 * The window-closing listener for the popup. 532 * 533 * @see WinListener 534 */ 535 protected WinListener popupListener; 536 537 private void ensurePopupMenuCreated() { 538 if (popupMenu == null) { 539 final JMenu thisMenu = this; 540 this.popupMenu = new JPopupMenu(); 541 popupMenu.setInvoker(this); 542 popupListener = createWinListener(popupMenu); 543 } 544 } 545 546 /* 547 * Return the customized location of the popup component. 548 */ 549 private Point getCustomMenuLocation() { 550 return customMenuLocation; 551 } 552 553 /** 554 * Sets the location of the popup component. 555 * 556 * @param x the x coordinate of the popup's new position 557 * @param y the y coordinate of the popup's new position 558 */ 559 public void setMenuLocation(int x, int y) { 560 customMenuLocation = new Point(x, y); 561 if (popupMenu != null) 562 popupMenu.setLocation(x, y); 563 } 564 565 /** 566 * Appends a menu item to the end of this menu. 567 * Returns the menu item added. 568 * 569 * @param menuItem the <code>JMenuitem</code> to be added 570 * @return the <code>JMenuItem</code> added 571 */ 572 public JMenuItem add(JMenuItem menuItem) { 573 ensurePopupMenuCreated(); 574 return popupMenu.add(menuItem); 575 } 576 577 /** 578 * Appends a component to the end of this menu. 579 * Returns the component added. 580 * 581 * @param c the <code>Component</code> to add 582 * @return the <code>Component</code> added 583 */ 584 public Component add(Component c) { 585 ensurePopupMenuCreated(); 586 popupMenu.add(c); 587 return c; 588 } 589 590 /** 591 * Adds the specified component to this container at the given 592 * position. If <code>index</code> equals -1, the component will 593 * be appended to the end. 594 * @param c the <code>Component</code> to add 595 * @param index the position at which to insert the component 596 * @return the <code>Component</code> added 597 * @see #remove 598 * @see java.awt.Container#add(Component, int) 599 */ 600 public Component add(Component c, int index) { 601 ensurePopupMenuCreated(); 602 popupMenu.add(c, index); 603 return c; 604 } 605 606 /** 607 * Creates a new menu item with the specified text and appends 608 * it to the end of this menu. 609 * 610 * @param s the string for the menu item to be added 611 * @return the new {@code JMenuItem} 612 */ 613 public JMenuItem add(String s) { 614 return add(new JMenuItem(s)); 615 } 616 617 /** 618 * Creates a new menu item attached to the specified {@code Action} object 619 * and appends it to the end of this menu. 620 * 621 * @param a the {@code Action} for the menu item to be added 622 * @return the new {@code JMenuItem} 623 * @see Action 624 */ 625 public JMenuItem add(Action a) { 626 JMenuItem mi = createActionComponent(a); 627 mi.setAction(a); 628 add(mi); 629 return mi; 630 } 631 632 /** 633 * Factory method which creates the <code>JMenuItem</code> for 634 * <code>Action</code>s added to the <code>JMenu</code>. 635 * 636 * @param a the <code>Action</code> for the menu item to be added 637 * @return the new menu item 638 * @see Action 639 * 640 * @since 1.3 641 */ 642 protected JMenuItem createActionComponent(Action a) { 643 JMenuItem mi = new JMenuItem() { 644 protected PropertyChangeListener createActionPropertyChangeListener(Action a) { 645 PropertyChangeListener pcl = createActionChangeListener(this); 646 if (pcl == null) { 647 pcl = super.createActionPropertyChangeListener(a); 648 } 649 return pcl; 650 } 651 }; 652 mi.setHorizontalTextPosition(JButton.TRAILING); 653 mi.setVerticalTextPosition(JButton.CENTER); 654 return mi; 655 } 656 657 /** 658 * Returns a properly configured {@code PropertyChangeListener} 659 * which updates the control as changes to the {@code Action} occur. 660 * 661 * @param b a menu item for which to create a {@code PropertyChangeListener} 662 * @return a {@code PropertyChangeListener} for {@code b} 663 */ 664 protected PropertyChangeListener createActionChangeListener(JMenuItem b) { 665 return b.createActionPropertyChangeListener0(b.getAction()); 666 } 667 668 /** 669 * Appends a new separator to the end of the menu. 670 */ 671 public void addSeparator() 672 { 673 ensurePopupMenuCreated(); 674 popupMenu.addSeparator(); 675 } 676 677 /** 678 * Inserts a new menu item with the specified text at a 679 * given position. 680 * 681 * @param s the text for the menu item to add 682 * @param pos an integer specifying the position at which to add the 683 * new menu item 684 * @exception IllegalArgumentException when the value of 685 * <code>pos</code> < 0 686 */ 687 public void insert(String s, int pos) { 688 if (pos < 0) { 689 throw new IllegalArgumentException("index less than zero."); 690 } 691 692 ensurePopupMenuCreated(); 693 popupMenu.insert(new JMenuItem(s), pos); 694 } 695 696 /** 697 * Inserts the specified <code>JMenuitem</code> at a given position. 698 * 699 * @param mi the <code>JMenuitem</code> to add 700 * @param pos an integer specifying the position at which to add the 701 * new <code>JMenuitem</code> 702 * @return the new menu item 703 * @exception IllegalArgumentException if the value of 704 * <code>pos</code> < 0 705 */ 706 public JMenuItem insert(JMenuItem mi, int pos) { 707 if (pos < 0) { 708 throw new IllegalArgumentException("index less than zero."); 709 } 710 ensurePopupMenuCreated(); 711 popupMenu.insert(mi, pos); 712 return mi; 713 } 714 715 /** 716 * Inserts a new menu item attached to the specified <code>Action</code> 717 * object at a given position. 718 * 719 * @param a the <code>Action</code> object for the menu item to add 720 * @param pos an integer specifying the position at which to add the 721 * new menu item 722 * @return the new menu item 723 * @exception IllegalArgumentException if the value of 724 * <code>pos</code> < 0 725 */ 726 public JMenuItem insert(Action a, int pos) { 727 if (pos < 0) { 728 throw new IllegalArgumentException("index less than zero."); 729 } 730 731 ensurePopupMenuCreated(); 732 JMenuItem mi = new JMenuItem(a); 733 mi.setHorizontalTextPosition(JButton.TRAILING); 734 mi.setVerticalTextPosition(JButton.CENTER); 735 popupMenu.insert(mi, pos); 736 return mi; 737 } 738 739 /** 740 * Inserts a separator at the specified position. 741 * 742 * @param index an integer specifying the position at which to 743 * insert the menu separator 744 * @exception IllegalArgumentException if the value of 745 * <code>index</code> < 0 746 */ 747 public void insertSeparator(int index) { 748 if (index < 0) { 749 throw new IllegalArgumentException("index less than zero."); 750 } 751 752 ensurePopupMenuCreated(); 753 popupMenu.insert( new JPopupMenu.Separator(), index ); 754 } 755 756 /** 757 * Returns the {@code JMenuItem} at the specified position. 758 * If the component at {@code pos} is not a menu item, 759 * {@code null} is returned. 760 * This method is included for AWT compatibility. 761 * 762 * @param pos an integer specifying the position 763 * @return the menu item at the specified position; or <code>null</code> 764 * if the item as the specified position is not a menu item 765 * @exception IllegalArgumentException if the value of 766 * {@code pos} < 0 767 */ 768 public JMenuItem getItem(int pos) { 769 if (pos < 0) { 770 throw new IllegalArgumentException("index less than zero."); 771 } 772 773 Component c = getMenuComponent(pos); 774 if (c instanceof JMenuItem) { 775 JMenuItem mi = (JMenuItem) c; 776 return mi; 777 } 778 779 // 4173633 780 return null; 781 } 782 783 /** 784 * Returns the number of items on the menu, including separators. 785 * This method is included for AWT compatibility. 786 * 787 * @return an integer equal to the number of items on the menu 788 * @see #getMenuComponentCount 789 */ 790 public int getItemCount() { 791 return getMenuComponentCount(); 792 } 793 794 /** 795 * Returns true if the menu can be torn off. This method is not 796 * yet implemented. 797 * 798 * @return true if the menu can be torn off, else false 799 * @exception Error if invoked -- this method is not yet implemented 800 */ 801 public boolean isTearOff() { 802 throw new Error("boolean isTearOff() {} not yet implemented"); 803 } 804 805 /** 806 * Removes the specified menu item from this menu. If there is no 807 * popup menu, this method will have no effect. 808 * 809 * @param item the <code>JMenuItem</code> to be removed from the menu 810 */ 811 public void remove(JMenuItem item) { 812 if (popupMenu != null) 813 popupMenu.remove(item); 814 } 815 816 /** 817 * Removes the menu item at the specified index from this menu. 818 * 819 * @param pos the position of the item to be removed 820 * @exception IllegalArgumentException if the value of 821 * <code>pos</code> < 0, or if <code>pos</code> 822 * is greater than the number of menu items 823 */ 824 public void remove(int pos) { 825 if (pos < 0) { 826 throw new IllegalArgumentException("index less than zero."); 827 } 828 if (pos > getItemCount()) { 829 throw new IllegalArgumentException("index greater than the number of items."); 830 } 831 if (popupMenu != null) 832 popupMenu.remove(pos); 833 } 834 835 /** 836 * Removes the component <code>c</code> from this menu. 837 * 838 * @param c the component to be removed 839 */ 840 public void remove(Component c) { 841 if (popupMenu != null) 842 popupMenu.remove(c); 843 } 844 845 /** 846 * Removes all menu items from this menu. 847 */ 848 public void removeAll() { 849 if (popupMenu != null) 850 popupMenu.removeAll(); 851 } 852 853 /** 854 * Returns the number of components on the menu. 855 * 856 * @return an integer containing the number of components on the menu 857 */ 858 public int getMenuComponentCount() { 859 int componentCount = 0; 860 if (popupMenu != null) 861 componentCount = popupMenu.getComponentCount(); 862 return componentCount; 863 } 864 865 /** 866 * Returns the component at position <code>n</code>. 867 * 868 * @param n the position of the component to be returned 869 * @return the component requested, or <code>null</code> 870 * if there is no popup menu 871 * 872 */ 873 public Component getMenuComponent(int n) { 874 if (popupMenu != null) 875 return popupMenu.getComponent(n); 876 877 return null; 878 } 879 880 /** 881 * Returns an array of <code>Component</code>s of the menu's 882 * subcomponents. Note that this returns all <code>Component</code>s 883 * in the popup menu, including separators. 884 * 885 * @return an array of <code>Component</code>s or an empty array 886 * if there is no popup menu 887 */ 888 public Component[] getMenuComponents() { 889 if (popupMenu != null) 890 return popupMenu.getComponents(); 891 892 return new Component[0]; 893 } 894 895 /** 896 * Returns true if the menu is a 'top-level menu', that is, if it is 897 * the direct child of a menubar. 898 * 899 * @return true if the menu is activated from the menu bar; 900 * false if the menu is activated from a menu item 901 * on another menu 902 */ 903 public boolean isTopLevelMenu() { 904 return getParent() instanceof JMenuBar; 905 906 } 907 908 /** 909 * Returns true if the specified component exists in the 910 * submenu hierarchy. 911 * 912 * @param c the <code>Component</code> to be tested 913 * @return true if the <code>Component</code> exists, false otherwise 914 */ 915 public boolean isMenuComponent(Component c) { 916 // Are we in the MenuItem part of the menu 917 if (c == this) 918 return true; 919 // Are we in the PopupMenu? 920 if (c instanceof JPopupMenu) { 921 JPopupMenu comp = (JPopupMenu) c; 922 if (comp == this.getPopupMenu()) 923 return true; 924 } 925 // Are we in a Component on the PopupMenu 926 int ncomponents = this.getMenuComponentCount(); 927 Component[] component = this.getMenuComponents(); 928 for (int i = 0 ; i < ncomponents ; i++) { 929 Component comp = component[i]; 930 // Are we in the current component? 931 if (comp == c) 932 return true; 933 // Hmmm, what about Non-menu containers? 934 935 // Recursive call for the Menu case 936 if (comp instanceof JMenu) { 937 JMenu subMenu = (JMenu) comp; 938 if (subMenu.isMenuComponent(c)) 939 return true; 940 } 941 } 942 return false; 943 } 944 945 946 /* 947 * Returns a point in the coordinate space of this menu's popupmenu 948 * which corresponds to the point <code>p</code> in the menu's 949 * coordinate space. 950 * 951 * @param p the point to be translated 952 * @return the point in the coordinate space of this menu's popupmenu 953 */ 954 private Point translateToPopupMenu(Point p) { 955 return translateToPopupMenu(p.x, p.y); 956 } 957 958 /* 959 * Returns a point in the coordinate space of this menu's popupmenu 960 * which corresponds to the point (x,y) in the menu's coordinate space. 961 * 962 * @param x the x coordinate of the point to be translated 963 * @param y the y coordinate of the point to be translated 964 * @return the point in the coordinate space of this menu's popupmenu 965 */ 966 private Point translateToPopupMenu(int x, int y) { 967 int newX; 968 int newY; 969 970 if (getParent() instanceof JPopupMenu) { 971 newX = x - getSize().width; 972 newY = y; 973 } else { 974 newX = x; 975 newY = y - getSize().height; 976 } 977 978 return new Point(newX, newY); 979 } 980 981 /** 982 * Returns the popupmenu associated with this menu. If there is 983 * no popupmenu, it will create one. 984 * 985 * @return the {@code JPopupMenu} associated with this menu 986 */ 987 public JPopupMenu getPopupMenu() { 988 ensurePopupMenuCreated(); 989 return popupMenu; 990 } 991 992 /** 993 * Adds a listener for menu events. 994 * 995 * @param l the listener to be added 996 */ 997 public void addMenuListener(MenuListener l) { 998 listenerList.add(MenuListener.class, l); 999 } 1000 1001 /** 1002 * Removes a listener for menu events. 1003 * 1004 * @param l the listener to be removed 1005 */ 1006 public void removeMenuListener(MenuListener l) { 1007 listenerList.remove(MenuListener.class, l); 1008 } 1009 1010 /** 1011 * Returns an array of all the <code>MenuListener</code>s added 1012 * to this JMenu with addMenuListener(). 1013 * 1014 * @return all of the <code>MenuListener</code>s added or an empty 1015 * array if no listeners have been added 1016 * @since 1.4 1017 */ 1018 public MenuListener[] getMenuListeners() { 1019 return listenerList.getListeners(MenuListener.class); 1020 } 1021 1022 /** 1023 * Notifies all listeners that have registered interest for 1024 * notification on this event type. The event instance 1025 * is created lazily. 1026 * 1027 * @exception Error if there is a <code>null</code> listener 1028 * @see EventListenerList 1029 */ 1030 protected void fireMenuSelected() { 1031 if (DEBUG) { 1032 System.out.println("In JMenu.fireMenuSelected"); 1033 } 1034 // Guaranteed to return a non-null array 1035 Object[] listeners = listenerList.getListenerList(); 1036 // Process the listeners last to first, notifying 1037 // those that are interested in this event 1038 for (int i = listeners.length-2; i>=0; i-=2) { 1039 if (listeners[i]==MenuListener.class) { 1040 if (listeners[i+1]== null) { 1041 throw new Error(getText() +" has a NULL Listener!! " + i); 1042 } else { 1043 // Lazily create the event: 1044 if (menuEvent == null) 1045 menuEvent = new MenuEvent(this); 1046 ((MenuListener)listeners[i+1]).menuSelected(menuEvent); 1047 } 1048 } 1049 } 1050 } 1051 1052 /** 1053 * Notifies all listeners that have registered interest for 1054 * notification on this event type. The event instance 1055 * is created lazily. 1056 * 1057 * @exception Error if there is a <code>null</code> listener 1058 * @see EventListenerList 1059 */ 1060 protected void fireMenuDeselected() { 1061 if (DEBUG) { 1062 System.out.println("In JMenu.fireMenuDeselected"); 1063 } 1064 // Guaranteed to return a non-null array 1065 Object[] listeners = listenerList.getListenerList(); 1066 // Process the listeners last to first, notifying 1067 // those that are interested in this event 1068 for (int i = listeners.length-2; i>=0; i-=2) { 1069 if (listeners[i]==MenuListener.class) { 1070 if (listeners[i+1]== null) { 1071 throw new Error(getText() +" has a NULL Listener!! " + i); 1072 } else { 1073 // Lazily create the event: 1074 if (menuEvent == null) 1075 menuEvent = new MenuEvent(this); 1076 ((MenuListener)listeners[i+1]).menuDeselected(menuEvent); 1077 } 1078 } 1079 } 1080 } 1081 1082 /** 1083 * Notifies all listeners that have registered interest for 1084 * notification on this event type. The event instance 1085 * is created lazily. 1086 * 1087 * @exception Error if there is a <code>null</code> listener 1088 * @see EventListenerList 1089 */ 1090 protected void fireMenuCanceled() { 1091 if (DEBUG) { 1092 System.out.println("In JMenu.fireMenuCanceled"); 1093 } 1094 // Guaranteed to return a non-null array 1095 Object[] listeners = listenerList.getListenerList(); 1096 // Process the listeners last to first, notifying 1097 // those that are interested in this event 1098 for (int i = listeners.length-2; i>=0; i-=2) { 1099 if (listeners[i]==MenuListener.class) { 1100 if (listeners[i+1]== null) { 1101 throw new Error(getText() +" has a NULL Listener!! " 1102 + i); 1103 } else { 1104 // Lazily create the event: 1105 if (menuEvent == null) 1106 menuEvent = new MenuEvent(this); 1107 ((MenuListener)listeners[i+1]).menuCanceled(menuEvent); 1108 } 1109 } 1110 } 1111 } 1112 1113 // Overriden to do nothing, JMenu doesn't support an accelerator 1114 void configureAcceleratorFromAction(Action a) { 1115 } 1116 1117 @SuppressWarnings("serial") 1118 class MenuChangeListener implements ChangeListener, Serializable { 1119 boolean isSelected = false; 1120 public void stateChanged(ChangeEvent e) { 1121 ButtonModel model = (ButtonModel) e.getSource(); 1122 boolean modelSelected = model.isSelected(); 1123 1124 if (modelSelected != isSelected) { 1125 if (modelSelected == true) { 1126 fireMenuSelected(); 1127 } else { 1128 fireMenuDeselected(); 1129 } 1130 isSelected = modelSelected; 1131 } 1132 } 1133 } 1134 1135 private ChangeListener createMenuChangeListener() { 1136 return new MenuChangeListener(); 1137 } 1138 1139 1140 /** 1141 * Creates a window-closing listener for the popup. 1142 * 1143 * @param p the <code>JPopupMenu</code> 1144 * @return the new window-closing listener 1145 * 1146 * @see WinListener 1147 */ 1148 protected WinListener createWinListener(JPopupMenu p) { 1149 return new WinListener(p); 1150 } 1151 1152 /** 1153 * A listener class that watches for a popup window closing. 1154 * When the popup is closing, the listener deselects the menu. 1155 * <p> 1156 * <strong>Warning:</strong> 1157 * Serialized objects of this class will not be compatible with 1158 * future Swing releases. The current serialization support is 1159 * appropriate for short term storage or RMI between applications running 1160 * the same version of Swing. As of 1.4, support for long term storage 1161 * of all JavaBeans™ 1162 * has been added to the <code>java.beans</code> package. 1163 * Please see {@link java.beans.XMLEncoder}. 1164 */ 1165 @SuppressWarnings("serial") 1166 protected class WinListener extends WindowAdapter implements Serializable { 1167 JPopupMenu popupMenu; 1168 /** 1169 * Create the window listener for the specified popup. 1170 * 1171 * @param p the popup menu for which to create a listener 1172 * @since 1.4 1173 */ 1174 public WinListener(JPopupMenu p) { 1175 this.popupMenu = p; 1176 } 1177 /** 1178 * Deselect the menu when the popup is closed from outside. 1179 */ 1180 public void windowClosing(WindowEvent e) { 1181 setSelected(false); 1182 } 1183 } 1184 1185 /** 1186 * Messaged when the menubar selection changes to activate or 1187 * deactivate this menu. 1188 * Overrides <code>JMenuItem.menuSelectionChanged</code>. 1189 * 1190 * @param isIncluded true if this menu is active, false if 1191 * it is not 1192 */ 1193 public void menuSelectionChanged(boolean isIncluded) { 1194 if (DEBUG) { 1195 System.out.println("In JMenu.menuSelectionChanged to " + isIncluded); 1196 } 1197 setSelected(isIncluded); 1198 } 1199 1200 /** 1201 * Returns an array of <code>MenuElement</code>s containing the submenu 1202 * for this menu component. If popup menu is <code>null</code> returns 1203 * an empty array. This method is required to conform to the 1204 * <code>MenuElement</code> interface. Note that since 1205 * <code>JSeparator</code>s do not conform to the <code>MenuElement</code> 1206 * interface, this array will only contain <code>JMenuItem</code>s. 1207 * 1208 * @return an array of <code>MenuElement</code> objects 1209 */ 1210 public MenuElement[] getSubElements() { 1211 if(popupMenu == null) 1212 return new MenuElement[0]; 1213 else { 1214 MenuElement result[] = new MenuElement[1]; 1215 result[0] = popupMenu; 1216 return result; 1217 } 1218 } 1219 1220 1221 // implements javax.swing.MenuElement 1222 /** 1223 * Returns the <code>java.awt.Component</code> used to 1224 * paint this <code>MenuElement</code>. 1225 * The returned component is used to convert events and detect if 1226 * an event is inside a menu component. 1227 */ 1228 public Component getComponent() { 1229 return this; 1230 } 1231 1232 1233 /** 1234 * Sets the <code>ComponentOrientation</code> property of this menu 1235 * and all components contained within it. This includes all 1236 * components returned by {@link #getMenuComponents getMenuComponents}. 1237 * 1238 * @param o the new component orientation of this menu and 1239 * the components contained within it. 1240 * @exception NullPointerException if <code>orientation</code> is null. 1241 * @see java.awt.Component#setComponentOrientation 1242 * @see java.awt.Component#getComponentOrientation 1243 * @since 1.4 1244 */ 1245 public void applyComponentOrientation(ComponentOrientation o) { 1246 super.applyComponentOrientation(o); 1247 1248 if ( popupMenu != null ) { 1249 int ncomponents = getMenuComponentCount(); 1250 for (int i = 0 ; i < ncomponents ; ++i) { 1251 getMenuComponent(i).applyComponentOrientation(o); 1252 } 1253 popupMenu.setComponentOrientation(o); 1254 } 1255 } 1256 1257 public void setComponentOrientation(ComponentOrientation o) { 1258 super.setComponentOrientation(o); 1259 if ( popupMenu != null ) { 1260 popupMenu.setComponentOrientation(o); 1261 } 1262 } 1263 1264 /** 1265 * <code>setAccelerator</code> is not defined for <code>JMenu</code>. 1266 * Use <code>setMnemonic</code> instead. 1267 * @param keyStroke the keystroke combination which will invoke 1268 * the <code>JMenuItem</code>'s actionlisteners 1269 * without navigating the menu hierarchy 1270 * @exception Error if invoked -- this method is not defined for JMenu. 1271 * Use <code>setMnemonic</code> instead 1272 * 1273 * @beaninfo 1274 * description: The keystroke combination which will invoke the JMenuItem's 1275 * actionlisteners without navigating the menu hierarchy 1276 * hidden: true 1277 */ 1278 public void setAccelerator(KeyStroke keyStroke) { 1279 throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead."); 1280 } 1281 1282 /** 1283 * Processes key stroke events such as mnemonics and accelerators. 1284 * 1285 * @param evt the key event to be processed 1286 */ 1287 protected void processKeyEvent(KeyEvent evt) { 1288 MenuSelectionManager.defaultManager().processKeyEvent(evt); 1289 if (evt.isConsumed()) 1290 return; 1291 1292 super.processKeyEvent(evt); 1293 } 1294 1295 /** 1296 * Programmatically performs a "click". This overrides the method 1297 * <code>AbstractButton.doClick</code> in order to make the menu pop up. 1298 * @param pressTime indicates the number of milliseconds the 1299 * button was pressed for 1300 */ 1301 public void doClick(int pressTime) { 1302 MenuElement me[] = buildMenuElementArray(this); 1303 MenuSelectionManager.defaultManager().setSelectedPath(me); 1304 } 1305 1306 /* 1307 * Build an array of menu elements - from <code>PopupMenu</code> to 1308 * the root <code>JMenuBar</code>. 1309 * @param leaf the leaf node from which to start building up the array 1310 * @return the array of menu items 1311 */ 1312 private MenuElement[] buildMenuElementArray(JMenu leaf) { 1313 Vector<MenuElement> elements = new Vector<MenuElement>(); 1314 Component current = leaf.getPopupMenu(); 1315 JPopupMenu pop; 1316 JMenu menu; 1317 JMenuBar bar; 1318 1319 while (true) { 1320 if (current instanceof JPopupMenu) { 1321 pop = (JPopupMenu) current; 1322 elements.insertElementAt(pop, 0); 1323 current = pop.getInvoker(); 1324 } else if (current instanceof JMenu) { 1325 menu = (JMenu) current; 1326 elements.insertElementAt(menu, 0); 1327 current = menu.getParent(); 1328 } else if (current instanceof JMenuBar) { 1329 bar = (JMenuBar) current; 1330 elements.insertElementAt(bar, 0); 1331 MenuElement me[] = new MenuElement[elements.size()]; 1332 elements.copyInto(me); 1333 return me; 1334 } 1335 } 1336 } 1337 1338 1339 /** 1340 * See <code>readObject</code> and <code>writeObject</code> in 1341 * <code>JComponent</code> for more 1342 * information about serialization in Swing. 1343 */ 1344 private void writeObject(ObjectOutputStream s) throws IOException { 1345 s.defaultWriteObject(); 1346 if (getUIClassID().equals(uiClassID)) { 1347 byte count = JComponent.getWriteObjCounter(this); 1348 JComponent.setWriteObjCounter(this, --count); 1349 if (count == 0 && ui != null) { 1350 ui.installUI(this); 1351 } 1352 } 1353 } 1354 1355 1356 /** 1357 * Returns a string representation of this <code>JMenu</code>. This 1358 * method is intended to be used only for debugging purposes, and the 1359 * content and format of the returned string may vary between 1360 * implementations. The returned string may be empty but may not 1361 * be <code>null</code>. 1362 * 1363 * @return a string representation of this JMenu. 1364 */ 1365 protected String paramString() { 1366 return super.paramString(); 1367 } 1368 1369 1370 ///////////////// 1371 // Accessibility support 1372 //////////////// 1373 1374 /** 1375 * Gets the AccessibleContext associated with this JMenu. 1376 * For JMenus, the AccessibleContext takes the form of an 1377 * AccessibleJMenu. 1378 * A new AccessibleJMenu instance is created if necessary. 1379 * 1380 * @return an AccessibleJMenu that serves as the 1381 * AccessibleContext of this JMenu 1382 */ 1383 public AccessibleContext getAccessibleContext() { 1384 if (accessibleContext == null) { 1385 accessibleContext = new AccessibleJMenu(); 1386 } 1387 return accessibleContext; 1388 } 1389 1390 /** 1391 * This class implements accessibility support for the 1392 * <code>JMenu</code> class. It provides an implementation of the 1393 * Java Accessibility API appropriate to menu user-interface elements. 1394 * <p> 1395 * <strong>Warning:</strong> 1396 * Serialized objects of this class will not be compatible with 1397 * future Swing releases. The current serialization support is 1398 * appropriate for short term storage or RMI between applications running 1399 * the same version of Swing. As of 1.4, support for long term storage 1400 * of all JavaBeans™ 1401 * has been added to the <code>java.beans</code> package. 1402 * Please see {@link java.beans.XMLEncoder}. 1403 */ 1404 @SuppressWarnings("serial") 1405 protected class AccessibleJMenu extends AccessibleJMenuItem 1406 implements AccessibleSelection { 1407 1408 /** 1409 * Returns the number of accessible children in the object. If all 1410 * of the children of this object implement Accessible, than this 1411 * method should return the number of children of this object. 1412 * 1413 * @return the number of accessible children in the object. 1414 */ 1415 public int getAccessibleChildrenCount() { 1416 Component[] children = getMenuComponents(); 1417 int count = 0; 1418 for (Component child : children) { 1419 if (child instanceof Accessible) { 1420 count++; 1421 } 1422 } 1423 return count; 1424 } 1425 1426 /** 1427 * Returns the nth Accessible child of the object. 1428 * 1429 * @param i zero-based index of child 1430 * @return the nth Accessible child of the object 1431 */ 1432 public Accessible getAccessibleChild(int i) { 1433 Component[] children = getMenuComponents(); 1434 int count = 0; 1435 for (Component child : children) { 1436 if (child instanceof Accessible) { 1437 if (count == i) { 1438 if (child instanceof JComponent) { 1439 // FIXME: [[[WDW - probably should set this when 1440 // the component is added to the menu. I tried 1441 // to do this in most cases, but the separators 1442 // added by addSeparator are hard to get to.]]] 1443 AccessibleContext ac = child.getAccessibleContext(); 1444 ac.setAccessibleParent(JMenu.this); 1445 } 1446 return (Accessible) child; 1447 } else { 1448 count++; 1449 } 1450 } 1451 } 1452 return null; 1453 } 1454 1455 /** 1456 * Get the role of this object. 1457 * 1458 * @return an instance of AccessibleRole describing the role of the 1459 * object 1460 * @see AccessibleRole 1461 */ 1462 public AccessibleRole getAccessibleRole() { 1463 return AccessibleRole.MENU; 1464 } 1465 1466 /** 1467 * Get the AccessibleSelection associated with this object. In the 1468 * implementation of the Java Accessibility API for this class, 1469 * return this object, which is responsible for implementing the 1470 * AccessibleSelection interface on behalf of itself. 1471 * 1472 * @return this object 1473 */ 1474 public AccessibleSelection getAccessibleSelection() { 1475 return this; 1476 } 1477 1478 /** 1479 * Returns 1 if a sub-menu is currently selected in this menu. 1480 * 1481 * @return 1 if a menu is currently selected, else 0 1482 */ 1483 public int getAccessibleSelectionCount() { 1484 MenuElement me[] = 1485 MenuSelectionManager.defaultManager().getSelectedPath(); 1486 if (me != null) { 1487 for (int i = 0; i < me.length; i++) { 1488 if (me[i] == JMenu.this) { // this menu is selected 1489 if (i+1 < me.length) { 1490 return 1; 1491 } 1492 } 1493 } 1494 } 1495 return 0; 1496 } 1497 1498 /** 1499 * Returns the currently selected sub-menu if one is selected, 1500 * otherwise null (there can only be one selection, and it can 1501 * only be a sub-menu, as otherwise menu items don't remain 1502 * selected). 1503 */ 1504 public Accessible getAccessibleSelection(int i) { 1505 // if i is a sub-menu & popped, return it 1506 if (i < 0 || i >= getItemCount()) { 1507 return null; 1508 } 1509 MenuElement me[] = 1510 MenuSelectionManager.defaultManager().getSelectedPath(); 1511 if (me != null) { 1512 for (int j = 0; j < me.length; j++) { 1513 if (me[j] == JMenu.this) { // this menu is selected 1514 // so find the next JMenuItem in the MenuElement 1515 // array, and return it! 1516 while (++j < me.length) { 1517 if (me[j] instanceof JMenuItem) { 1518 return (Accessible) me[j]; 1519 } 1520 } 1521 } 1522 } 1523 } 1524 return null; 1525 } 1526 1527 /** 1528 * Returns true if the current child of this object is selected 1529 * (that is, if this child is a popped-up submenu). 1530 * 1531 * @param i the zero-based index of the child in this Accessible 1532 * object. 1533 * @see AccessibleContext#getAccessibleChild 1534 */ 1535 public boolean isAccessibleChildSelected(int i) { 1536 // if i is a sub-menu and is pop-ed up, return true, else false 1537 MenuElement me[] = 1538 MenuSelectionManager.defaultManager().getSelectedPath(); 1539 if (me != null) { 1540 JMenuItem mi = JMenu.this.getItem(i); 1541 for (int j = 0; j < me.length; j++) { 1542 if (me[j] == mi) { 1543 return true; 1544 } 1545 } 1546 } 1547 return false; 1548 } 1549 1550 1551 /** 1552 * Selects the <code>i</code>th menu in the menu. 1553 * If that item is a submenu, 1554 * it will pop up in response. If a different item is already 1555 * popped up, this will force it to close. If this is a sub-menu 1556 * that is already popped up (selected), this method has no 1557 * effect. 1558 * 1559 * @param i the index of the item to be selected 1560 * @see #getAccessibleStateSet 1561 */ 1562 public void addAccessibleSelection(int i) { 1563 if (i < 0 || i >= getItemCount()) { 1564 return; 1565 } 1566 JMenuItem mi = getItem(i); 1567 if (mi != null) { 1568 if (mi instanceof JMenu) { 1569 MenuElement me[] = buildMenuElementArray((JMenu) mi); 1570 MenuSelectionManager.defaultManager().setSelectedPath(me); 1571 } else { 1572 MenuSelectionManager.defaultManager().setSelectedPath(null); 1573 } 1574 } 1575 } 1576 1577 /** 1578 * Removes the nth item from the selection. In general, menus 1579 * can only have one item within them selected at a time 1580 * (e.g. one sub-menu popped open). 1581 * 1582 * @param i the zero-based index of the selected item 1583 */ 1584 public void removeAccessibleSelection(int i) { 1585 if (i < 0 || i >= getItemCount()) { 1586 return; 1587 } 1588 JMenuItem mi = getItem(i); 1589 if (mi != null && mi instanceof JMenu) { 1590 if (mi.isSelected()) { 1591 MenuElement old[] = 1592 MenuSelectionManager.defaultManager().getSelectedPath(); 1593 MenuElement me[] = new MenuElement[old.length-2]; 1594 for (int j = 0; j < old.length -2; j++) { 1595 me[j] = old[j]; 1596 } 1597 MenuSelectionManager.defaultManager().setSelectedPath(me); 1598 } 1599 } 1600 } 1601 1602 /** 1603 * Clears the selection in the object, so that nothing in the 1604 * object is selected. This will close any open sub-menu. 1605 */ 1606 public void clearAccessibleSelection() { 1607 // if this menu is selected, reset selection to only go 1608 // to this menu; else do nothing 1609 MenuElement old[] = 1610 MenuSelectionManager.defaultManager().getSelectedPath(); 1611 if (old != null) { 1612 for (int j = 0; j < old.length; j++) { 1613 if (old[j] == JMenu.this) { // menu is in the selection! 1614 MenuElement me[] = new MenuElement[j+1]; 1615 System.arraycopy(old, 0, me, 0, j); 1616 me[j] = JMenu.this.getPopupMenu(); 1617 MenuSelectionManager.defaultManager().setSelectedPath(me); 1618 } 1619 } 1620 } 1621 } 1622 1623 /** 1624 * Normally causes every selected item in the object to be selected 1625 * if the object supports multiple selections. This method 1626 * makes no sense in a menu bar, and so does nothing. 1627 */ 1628 public void selectAllAccessibleSelection() { 1629 } 1630 } // inner class AccessibleJMenu 1631 1632 }