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