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