1 /* 2 * Copyright (c) 1997, 2012, 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.*; 29 import java.awt.event.*; 30 import java.io.IOException; 31 import java.io.ObjectInputStream; 32 import java.io.ObjectOutputStream; 33 import java.io.Serializable; 34 import java.beans.*; 35 36 import java.util.Locale; 37 import java.util.Vector; 38 import java.util.Hashtable; 39 import javax.accessibility.*; 40 import javax.swing.plaf.PopupMenuUI; 41 import javax.swing.plaf.ComponentUI; 42 import javax.swing.plaf.basic.BasicComboPopup; 43 import javax.swing.event.*; 44 45 import sun.awt.SunToolkit; 46 import sun.security.util.SecurityConstants; 47 48 import java.applet.Applet; 49 50 /** 51 * An implementation of a popup menu -- a small window that pops up 52 * and displays a series of choices. A <code>JPopupMenu</code> is used for the 53 * menu that appears when the user selects an item on the menu bar. 54 * It is also used for "pull-right" menu that appears when the 55 * selects a menu item that activates it. Finally, a <code>JPopupMenu</code> 56 * can also be used anywhere else you want a menu to appear. For 57 * example, when the user right-clicks in a specified area. 58 * <p> 59 * For information and examples of using popup menus, see 60 * <a 61 href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a> 62 * in <em>The Java Tutorial.</em> 63 * <p> 64 * <strong>Warning:</strong> Swing is not thread safe. For more 65 * information see <a 66 * href="package-summary.html#threading">Swing's Threading 67 * Policy</a>. 68 * <p> 69 * <strong>Warning:</strong> 70 * Serialized objects of this class will not be compatible with 71 * future Swing releases. The current serialization support is 72 * appropriate for short term storage or RMI between applications running 73 * the same version of Swing. As of 1.4, support for long term storage 74 * of all JavaBeans<sup><font size="-2">TM</font></sup> 75 * has been added to the <code>java.beans</code> package. 76 * Please see {@link java.beans.XMLEncoder}. 77 * 78 * @beaninfo 79 * attribute: isContainer false 80 * description: A small window that pops up and displays a series of choices. 81 * 82 * @author Georges Saab 83 * @author David Karlton 84 * @author Arnaud Weber 85 */ 86 @SuppressWarnings("serial") 87 public class JPopupMenu extends JComponent implements Accessible,MenuElement { 88 89 /** 90 * @see #getUIClassID 91 * @see #readObject 92 */ 93 private static final String uiClassID = "PopupMenuUI"; 94 95 /** 96 * Key used in AppContext to determine if light way popups are the default. 97 */ 98 private static final Object defaultLWPopupEnabledKey = 99 new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey"); 100 101 /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */ 102 static boolean popupPostionFixDisabled = false; 103 104 static { 105 popupPostionFixDisabled = java.security.AccessController.doPrivileged( 106 new sun.security.action.GetPropertyAction( 107 "javax.swing.adjustPopupLocationToFit","")).equals("false"); 108 109 } 110 111 transient Component invoker; 112 transient Popup popup; 113 transient Frame frame; 114 private int desiredLocationX,desiredLocationY; 115 116 private String label = null; 117 private boolean paintBorder = true; 118 private Insets margin = null; 119 120 /** 121 * Used to indicate if lightweight popups should be used. 122 */ 123 private boolean lightWeightPopup = true; 124 125 /* 126 * Model for the selected subcontrol. 127 */ 128 private SingleSelectionModel selectionModel; 129 130 /* Lock object used in place of class object for synchronization. 131 * (4187686) 132 */ 133 private static final Object classLock = new Object(); 134 135 /* diagnostic aids -- should be false for production builds. */ 136 private static final boolean TRACE = false; // trace creates and disposes 137 private static final boolean VERBOSE = false; // show reuse hits/misses 138 private static final boolean DEBUG = false; // show bad params, misc. 139 140 /** 141 * Sets the default value of the <code>lightWeightPopupEnabled</code> 142 * property. 143 * 144 * @param aFlag <code>true</code> if popups can be lightweight, 145 * otherwise <code>false</code> 146 * @see #getDefaultLightWeightPopupEnabled 147 * @see #setLightWeightPopupEnabled 148 */ 149 public static void setDefaultLightWeightPopupEnabled(boolean aFlag) { 150 SwingUtilities.appContextPut(defaultLWPopupEnabledKey, 151 Boolean.valueOf(aFlag)); 152 } 153 154 /** 155 * Gets the <code>defaultLightWeightPopupEnabled</code> property, 156 * which by default is <code>true</code>. 157 * 158 * @return the value of the <code>defaultLightWeightPopupEnabled</code> 159 * property 160 * 161 * @see #setDefaultLightWeightPopupEnabled 162 */ 163 public static boolean getDefaultLightWeightPopupEnabled() { 164 Boolean b = (Boolean) 165 SwingUtilities.appContextGet(defaultLWPopupEnabledKey); 166 if (b == null) { 167 SwingUtilities.appContextPut(defaultLWPopupEnabledKey, 168 Boolean.TRUE); 169 return true; 170 } 171 return b.booleanValue(); 172 } 173 174 /** 175 * Constructs a <code>JPopupMenu</code> without an "invoker". 176 */ 177 public JPopupMenu() { 178 this(null); 179 } 180 181 /** 182 * Constructs a <code>JPopupMenu</code> with the specified title. 183 * 184 * @param label the string that a UI may use to display as a title 185 * for the popup menu. 186 */ 187 public JPopupMenu(String label) { 188 this.label = label; 189 lightWeightPopup = getDefaultLightWeightPopupEnabled(); 190 setSelectionModel(new DefaultSingleSelectionModel()); 191 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 192 setFocusTraversalKeysEnabled(false); 193 updateUI(); 194 } 195 196 197 198 /** 199 * Returns the look and feel (L&F) object that renders this component. 200 * 201 * @return the <code>PopupMenuUI</code> object that renders this component 202 */ 203 public PopupMenuUI getUI() { 204 return (PopupMenuUI)ui; 205 } 206 207 /** 208 * Sets the L&F object that renders this component. 209 * 210 * @param ui the new <code>PopupMenuUI</code> L&F object 211 * @see UIDefaults#getUI 212 * @beaninfo 213 * bound: true 214 * hidden: true 215 * attribute: visualUpdate true 216 * description: The UI object that implements the Component's LookAndFeel. 217 */ 218 public void setUI(PopupMenuUI ui) { 219 super.setUI(ui); 220 } 221 222 /** 223 * Resets the UI property to a value from the current look and feel. 224 * 225 * @see JComponent#updateUI 226 */ 227 public void updateUI() { 228 setUI((PopupMenuUI)UIManager.getUI(this)); 229 } 230 231 232 /** 233 * Returns the name of the L&F class that renders this component. 234 * 235 * @return the string "PopupMenuUI" 236 * @see JComponent#getUIClassID 237 * @see UIDefaults#getUI 238 */ 239 public String getUIClassID() { 240 return uiClassID; 241 } 242 243 protected void processFocusEvent(FocusEvent evt) { 244 super.processFocusEvent(evt); 245 } 246 247 /** 248 * Processes key stroke events such as mnemonics and accelerators. 249 * 250 * @param evt the key event to be processed 251 */ 252 protected void processKeyEvent(KeyEvent evt) { 253 MenuSelectionManager.defaultManager().processKeyEvent(evt); 254 if (evt.isConsumed()) { 255 return; 256 } 257 super.processKeyEvent(evt); 258 } 259 260 261 /** 262 * Returns the model object that handles single selections. 263 * 264 * @return the <code>selectionModel</code> property 265 * @see SingleSelectionModel 266 */ 267 public SingleSelectionModel getSelectionModel() { 268 return selectionModel; 269 } 270 271 /** 272 * Sets the model object to handle single selections. 273 * 274 * @param model the new <code>SingleSelectionModel</code> 275 * @see SingleSelectionModel 276 * @beaninfo 277 * description: The selection model for the popup menu 278 * expert: true 279 */ 280 public void setSelectionModel(SingleSelectionModel model) { 281 selectionModel = model; 282 } 283 284 /** 285 * Appends the specified menu item to the end of this menu. 286 * 287 * @param menuItem the <code>JMenuItem</code> to add 288 * @return the <code>JMenuItem</code> added 289 */ 290 public JMenuItem add(JMenuItem menuItem) { 291 super.add(menuItem); 292 return menuItem; 293 } 294 295 /** 296 * Creates a new menu item with the specified text and appends 297 * it to the end of this menu. 298 * 299 * @param s the string for the menu item to be added 300 */ 301 public JMenuItem add(String s) { 302 return add(new JMenuItem(s)); 303 } 304 305 /** 306 * Appends a new menu item to the end of the menu which 307 * dispatches the specified <code>Action</code> object. 308 * 309 * @param a the <code>Action</code> to add to the menu 310 * @return the new menu item 311 * @see Action 312 */ 313 public JMenuItem add(Action a) { 314 JMenuItem mi = createActionComponent(a); 315 mi.setAction(a); 316 add(mi); 317 return mi; 318 } 319 320 /** 321 * Returns an point which has been adjusted to take into account of the 322 * desktop bounds, taskbar and multi-monitor configuration. 323 * <p> 324 * This adustment may be cancelled by invoking the application with 325 * -Djavax.swing.adjustPopupLocationToFit=false 326 */ 327 Point adjustPopupLocationToFitScreen(int xPosition, int yPosition) { 328 Point popupLocation = new Point(xPosition, yPosition); 329 330 if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless()) { 331 return popupLocation; 332 } 333 334 // Get screen bounds 335 Rectangle scrBounds; 336 GraphicsConfiguration gc = getCurrentGraphicsConfiguration(popupLocation); 337 Toolkit toolkit = Toolkit.getDefaultToolkit(); 338 if(gc != null) { 339 // If we have GraphicsConfiguration use it to get screen bounds 340 scrBounds = gc.getBounds(); 341 } else { 342 // If we don't have GraphicsConfiguration use primary screen 343 scrBounds = new Rectangle(toolkit.getScreenSize()); 344 } 345 346 // Calculate the screen size that popup should fit 347 Dimension popupSize = JPopupMenu.this.getPreferredSize(); 348 long popupRightX = (long)popupLocation.x + (long)popupSize.width; 349 long popupBottomY = (long)popupLocation.y + (long)popupSize.height; 350 int scrWidth = scrBounds.width; 351 int scrHeight = scrBounds.height; 352 353 if (!canPopupOverlapTaskBar()) { 354 // Insets include the task bar. Take them into account. 355 Insets scrInsets = toolkit.getScreenInsets(gc); 356 scrBounds.x += scrInsets.left; 357 scrBounds.y += scrInsets.top; 358 scrWidth -= scrInsets.left + scrInsets.right; 359 scrHeight -= scrInsets.top + scrInsets.bottom; 360 } 361 int scrRightX = scrBounds.x + scrWidth; 362 int scrBottomY = scrBounds.y + scrHeight; 363 364 // Ensure that popup menu fits the screen 365 if (popupRightX > (long) scrRightX) { 366 popupLocation.x = scrRightX - popupSize.width; 367 } 368 369 if (popupBottomY > (long) scrBottomY) { 370 popupLocation.y = scrBottomY - popupSize.height; 371 } 372 373 if (popupLocation.x < scrBounds.x) { 374 popupLocation.x = scrBounds.x; 375 } 376 377 if (popupLocation.y < scrBounds.y) { 378 popupLocation.y = scrBounds.y; 379 } 380 381 return popupLocation; 382 } 383 384 /** 385 * Tries to find GraphicsConfiguration 386 * that contains the mouse cursor position. 387 * Can return null. 388 */ 389 private GraphicsConfiguration getCurrentGraphicsConfiguration( 390 Point popupLocation) { 391 GraphicsConfiguration gc = null; 392 GraphicsEnvironment ge = 393 GraphicsEnvironment.getLocalGraphicsEnvironment(); 394 GraphicsDevice[] gd = ge.getScreenDevices(); 395 for(int i = 0; i < gd.length; i++) { 396 if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) { 397 GraphicsConfiguration dgc = 398 gd[i].getDefaultConfiguration(); 399 if(dgc.getBounds().contains(popupLocation)) { 400 gc = dgc; 401 break; 402 } 403 } 404 } 405 // If not found and we have invoker, ask invoker about his gc 406 if(gc == null && getInvoker() != null) { 407 gc = getInvoker().getGraphicsConfiguration(); 408 } 409 return gc; 410 } 411 412 /** 413 * Returns whether popup is allowed to be shown above the task bar. 414 */ 415 static boolean canPopupOverlapTaskBar() { 416 boolean result = true; 417 418 Toolkit tk = Toolkit.getDefaultToolkit(); 419 if (tk instanceof SunToolkit) { 420 result = ((SunToolkit)tk).canPopupOverlapTaskBar(); 421 } 422 423 return result; 424 } 425 426 /** 427 * Factory method which creates the <code>JMenuItem</code> for 428 * <code>Actions</code> added to the <code>JPopupMenu</code>. 429 * 430 * @param a the <code>Action</code> for the menu item to be added 431 * @return the new menu item 432 * @see Action 433 * 434 * @since 1.3 435 */ 436 protected JMenuItem createActionComponent(Action a) { 437 JMenuItem mi = new JMenuItem() { 438 protected PropertyChangeListener createActionPropertyChangeListener(Action a) { 439 PropertyChangeListener pcl = createActionChangeListener(this); 440 if (pcl == null) { 441 pcl = super.createActionPropertyChangeListener(a); 442 } 443 return pcl; 444 } 445 }; 446 mi.setHorizontalTextPosition(JButton.TRAILING); 447 mi.setVerticalTextPosition(JButton.CENTER); 448 return mi; 449 } 450 451 /** 452 * Returns a properly configured <code>PropertyChangeListener</code> 453 * which updates the control as changes to the <code>Action</code> occur. 454 */ 455 protected PropertyChangeListener createActionChangeListener(JMenuItem b) { 456 return b.createActionPropertyChangeListener0(b.getAction()); 457 } 458 459 /** 460 * Removes the component at the specified index from this popup menu. 461 * 462 * @param pos the position of the item to be removed 463 * @exception IllegalArgumentException if the value of 464 * <code>pos</code> < 0, or if the value of 465 * <code>pos</code> is greater than the 466 * number of items 467 */ 468 public void remove(int pos) { 469 if (pos < 0) { 470 throw new IllegalArgumentException("index less than zero."); 471 } 472 if (pos > getComponentCount() -1) { 473 throw new IllegalArgumentException("index greater than the number of items."); 474 } 475 super.remove(pos); 476 } 477 478 /** 479 * Sets the value of the <code>lightWeightPopupEnabled</code> property, 480 * which by default is <code>true</code>. 481 * By default, when a look and feel displays a popup, 482 * it can choose to 483 * use a lightweight (all-Java) popup. 484 * Lightweight popup windows are more efficient than heavyweight 485 * (native peer) windows, 486 * but lightweight and heavyweight components do not mix well in a GUI. 487 * If your application mixes lightweight and heavyweight components, 488 * you should disable lightweight popups. 489 * Some look and feels might always use heavyweight popups, 490 * no matter what the value of this property. 491 * 492 * @param aFlag <code>false</code> to disable lightweight popups 493 * @beaninfo 494 * description: Determines whether lightweight popups are used when possible 495 * expert: true 496 * 497 * @see #isLightWeightPopupEnabled 498 */ 499 public void setLightWeightPopupEnabled(boolean aFlag) { 500 // NOTE: this use to set the flag on a shared JPopupMenu, which meant 501 // this effected ALL JPopupMenus. 502 lightWeightPopup = aFlag; 503 } 504 505 /** 506 * Gets the <code>lightWeightPopupEnabled</code> property. 507 * 508 * @return the value of the <code>lightWeightPopupEnabled</code> property 509 * @see #setLightWeightPopupEnabled 510 */ 511 public boolean isLightWeightPopupEnabled() { 512 return lightWeightPopup; 513 } 514 515 /** 516 * Returns the popup menu's label 517 * 518 * @return a string containing the popup menu's label 519 * @see #setLabel 520 */ 521 public String getLabel() { 522 return label; 523 } 524 525 /** 526 * Sets the popup menu's label. Different look and feels may choose 527 * to display or not display this. 528 * 529 * @param label a string specifying the label for the popup menu 530 * 531 * @see #setLabel 532 * @beaninfo 533 * description: The label for the popup menu. 534 * bound: true 535 */ 536 public void setLabel(String label) { 537 String oldValue = this.label; 538 this.label = label; 539 firePropertyChange("label", oldValue, label); 540 if (accessibleContext != null) { 541 accessibleContext.firePropertyChange( 542 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 543 oldValue, label); 544 } 545 invalidate(); 546 repaint(); 547 } 548 549 /** 550 * Appends a new separator at the end of the menu. 551 */ 552 public void addSeparator() { 553 add( new JPopupMenu.Separator() ); 554 } 555 556 /** 557 * Inserts a menu item for the specified <code>Action</code> object at 558 * a given position. 559 * 560 * @param a the <code>Action</code> object to insert 561 * @param index specifies the position at which to insert the 562 * <code>Action</code>, where 0 is the first 563 * @exception IllegalArgumentException if <code>index</code> < 0 564 * @see Action 565 */ 566 public void insert(Action a, int index) { 567 JMenuItem mi = createActionComponent(a); 568 mi.setAction(a); 569 insert(mi, index); 570 } 571 572 /** 573 * Inserts the specified component into the menu at a given 574 * position. 575 * 576 * @param component the <code>Component</code> to insert 577 * @param index specifies the position at which 578 * to insert the component, where 0 is the first 579 * @exception IllegalArgumentException if <code>index</code> < 0 580 */ 581 public void insert(Component component, int index) { 582 if (index < 0) { 583 throw new IllegalArgumentException("index less than zero."); 584 } 585 586 int nitems = getComponentCount(); 587 // PENDING(ges): Why not use an array? 588 Vector<Component> tempItems = new Vector<Component>(); 589 590 /* Remove the item at index, nitems-index times 591 storing them in a temporary vector in the 592 order they appear on the menu. 593 */ 594 for (int i = index ; i < nitems; i++) { 595 tempItems.addElement(getComponent(index)); 596 remove(index); 597 } 598 599 add(component); 600 601 /* Add the removed items back to the menu, they are 602 already in the correct order in the temp vector. 603 */ 604 for (Component tempItem : tempItems) { 605 add(tempItem); 606 } 607 } 608 609 /** 610 * Adds a <code>PopupMenu</code> listener. 611 * 612 * @param l the <code>PopupMenuListener</code> to add 613 */ 614 public void addPopupMenuListener(PopupMenuListener l) { 615 listenerList.add(PopupMenuListener.class,l); 616 } 617 618 /** 619 * Removes a <code>PopupMenu</code> listener. 620 * 621 * @param l the <code>PopupMenuListener</code> to remove 622 */ 623 public void removePopupMenuListener(PopupMenuListener l) { 624 listenerList.remove(PopupMenuListener.class,l); 625 } 626 627 /** 628 * Returns an array of all the <code>PopupMenuListener</code>s added 629 * to this JMenuItem with addPopupMenuListener(). 630 * 631 * @return all of the <code>PopupMenuListener</code>s added or an empty 632 * array if no listeners have been added 633 * @since 1.4 634 */ 635 public PopupMenuListener[] getPopupMenuListeners() { 636 return listenerList.getListeners(PopupMenuListener.class); 637 } 638 639 /** 640 * Adds a <code>MenuKeyListener</code> to the popup menu. 641 * 642 * @param l the <code>MenuKeyListener</code> to be added 643 * @since 1.5 644 */ 645 public void addMenuKeyListener(MenuKeyListener l) { 646 listenerList.add(MenuKeyListener.class, l); 647 } 648 649 /** 650 * Removes a <code>MenuKeyListener</code> from the popup menu. 651 * 652 * @param l the <code>MenuKeyListener</code> to be removed 653 * @since 1.5 654 */ 655 public void removeMenuKeyListener(MenuKeyListener l) { 656 listenerList.remove(MenuKeyListener.class, l); 657 } 658 659 /** 660 * Returns an array of all the <code>MenuKeyListener</code>s added 661 * to this JPopupMenu with addMenuKeyListener(). 662 * 663 * @return all of the <code>MenuKeyListener</code>s added or an empty 664 * array if no listeners have been added 665 * @since 1.5 666 */ 667 public MenuKeyListener[] getMenuKeyListeners() { 668 return listenerList.getListeners(MenuKeyListener.class); 669 } 670 671 /** 672 * Notifies <code>PopupMenuListener</code>s that this popup menu will 673 * become visible. 674 */ 675 protected void firePopupMenuWillBecomeVisible() { 676 Object[] listeners = listenerList.getListenerList(); 677 PopupMenuEvent e=null; 678 for (int i = listeners.length-2; i>=0; i-=2) { 679 if (listeners[i]==PopupMenuListener.class) { 680 if (e == null) 681 e = new PopupMenuEvent(this); 682 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e); 683 } 684 } 685 } 686 687 /** 688 * Notifies <code>PopupMenuListener</code>s that this popup menu will 689 * become invisible. 690 */ 691 protected void firePopupMenuWillBecomeInvisible() { 692 Object[] listeners = listenerList.getListenerList(); 693 PopupMenuEvent e=null; 694 for (int i = listeners.length-2; i>=0; i-=2) { 695 if (listeners[i]==PopupMenuListener.class) { 696 if (e == null) 697 e = new PopupMenuEvent(this); 698 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e); 699 } 700 } 701 } 702 703 /** 704 * Notifies <code>PopupMenuListeners</code> that this popup menu is 705 * cancelled. 706 */ 707 protected void firePopupMenuCanceled() { 708 Object[] listeners = listenerList.getListenerList(); 709 PopupMenuEvent e=null; 710 for (int i = listeners.length-2; i>=0; i-=2) { 711 if (listeners[i]==PopupMenuListener.class) { 712 if (e == null) 713 e = new PopupMenuEvent(this); 714 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e); 715 } 716 } 717 } 718 719 /** 720 * Always returns true since popups, by definition, should always 721 * be on top of all other windows. 722 * @return true 723 */ 724 // package private 725 boolean alwaysOnTop() { 726 return true; 727 } 728 729 /** 730 * Lays out the container so that it uses the minimum space 731 * needed to display its contents. 732 */ 733 public void pack() { 734 if(popup != null) { 735 Dimension pref = getPreferredSize(); 736 737 if (pref == null || pref.width != getWidth() || 738 pref.height != getHeight()) { 739 popup = getPopup(); 740 } else { 741 validate(); 742 } 743 } 744 } 745 746 /** 747 * Sets the visibility of the popup menu. 748 * 749 * @param b true to make the popup visible, or false to 750 * hide it 751 * @beaninfo 752 * bound: true 753 * description: Makes the popup visible 754 */ 755 public void setVisible(boolean b) { 756 if (DEBUG) { 757 System.out.println("JPopupMenu.setVisible " + b); 758 } 759 760 // Is it a no-op? 761 if (b == isVisible()) 762 return; 763 764 // if closing, first close all Submenus 765 if (b == false) { 766 767 // 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is 768 // a protected method and cannot be called from BasicPopupMenuUI directly 769 // The real solution could be to make 770 // firePopupMenuCanceled public and call it directly. 771 Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled"); 772 if (doCanceled != null && doCanceled == Boolean.TRUE) { 773 putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE); 774 firePopupMenuCanceled(); 775 } 776 getSelectionModel().clearSelection(); 777 778 } else { 779 // This is a popup menu with MenuElement children, 780 // set selection path before popping up! 781 if (isPopupMenu()) { 782 MenuElement me[] = new MenuElement[1]; 783 me[0] = this; 784 MenuSelectionManager.defaultManager().setSelectedPath(me); 785 } 786 } 787 788 if(b) { 789 firePopupMenuWillBecomeVisible(); 790 popup = getPopup(); 791 firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE); 792 793 794 } else if(popup != null) { 795 firePopupMenuWillBecomeInvisible(); 796 popup.hide(); 797 popup = null; 798 firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE); 799 // 4694797: When popup menu is made invisible, selected path 800 // should be cleared 801 if (isPopupMenu()) { 802 MenuSelectionManager.defaultManager().clearSelectedPath(); 803 } 804 } 805 } 806 807 /** 808 * Returns a <code>Popup</code> instance from the 809 * <code>PopupMenuUI</code> that has had <code>show</code> invoked on 810 * it. If the current <code>popup</code> is non-null, 811 * this will invoke <code>dispose</code> of it, and then 812 * <code>show</code> the new one. 813 * <p> 814 * This does NOT fire any events, it is up the caller to dispatch 815 * the necessary events. 816 */ 817 private Popup getPopup() { 818 Popup oldPopup = popup; 819 820 if (oldPopup != null) { 821 oldPopup.hide(); 822 } 823 PopupFactory popupFactory = PopupFactory.getSharedInstance(); 824 825 if (isLightWeightPopupEnabled()) { 826 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP); 827 } 828 else { 829 popupFactory.setPopupType(PopupFactory.HEAVY_WEIGHT_POPUP); 830 } 831 832 // adjust the location of the popup 833 Point p = adjustPopupLocationToFitScreen(desiredLocationX,desiredLocationY); 834 desiredLocationX = p.x; 835 desiredLocationY = p.y; 836 837 Popup newPopup = getUI().getPopup(this, desiredLocationX, 838 desiredLocationY); 839 840 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP); 841 newPopup.show(); 842 return newPopup; 843 } 844 845 /** 846 * Returns true if the popup menu is visible (currently 847 * being displayed). 848 */ 849 public boolean isVisible() { 850 return popup != null; 851 } 852 853 /** 854 * Sets the location of the upper left corner of the 855 * popup menu using x, y coordinates. 856 * <p> 857 * The method changes the geometry-related data. Therefore, 858 * the native windowing system may ignore such requests, or it may modify 859 * the requested data, so that the {@code JPopupMenu} object is placed and sized 860 * in a way that corresponds closely to the desktop settings. 861 * 862 * @param x the x coordinate of the popup's new position 863 * in the screen's coordinate space 864 * @param y the y coordinate of the popup's new position 865 * in the screen's coordinate space 866 * @beaninfo 867 * description: The location of the popup menu. 868 */ 869 public void setLocation(int x, int y) { 870 int oldX = desiredLocationX; 871 int oldY = desiredLocationY; 872 873 desiredLocationX = x; 874 desiredLocationY = y; 875 if(popup != null && (x != oldX || y != oldY)) { 876 popup = getPopup(); 877 } 878 } 879 880 /** 881 * Returns true if the popup menu is a standalone popup menu 882 * rather than the submenu of a <code>JMenu</code>. 883 * 884 * @return true if this menu is a standalone popup menu, otherwise false 885 */ 886 private boolean isPopupMenu() { 887 return ((invoker != null) && !(invoker instanceof JMenu)); 888 } 889 890 /** 891 * Returns the component which is the 'invoker' of this 892 * popup menu. 893 * 894 * @return the <code>Component</code> in which the popup menu is displayed 895 */ 896 public Component getInvoker() { 897 return this.invoker; 898 } 899 900 /** 901 * Sets the invoker of this popup menu -- the component in which 902 * the popup menu menu is to be displayed. 903 * 904 * @param invoker the <code>Component</code> in which the popup 905 * menu is displayed 906 * @beaninfo 907 * description: The invoking component for the popup menu 908 * expert: true 909 */ 910 public void setInvoker(Component invoker) { 911 Component oldInvoker = this.invoker; 912 this.invoker = invoker; 913 if ((oldInvoker != this.invoker) && (ui != null)) { 914 ui.uninstallUI(this); 915 ui.installUI(this); 916 } 917 invalidate(); 918 } 919 920 /** 921 * Displays the popup menu at the position x,y in the coordinate 922 * space of the component invoker. 923 * 924 * @param invoker the component in whose space the popup menu is to appear 925 * @param x the x coordinate in invoker's coordinate space at which 926 * the popup menu is to be displayed 927 * @param y the y coordinate in invoker's coordinate space at which 928 * the popup menu is to be displayed 929 */ 930 public void show(Component invoker, int x, int y) { 931 if (DEBUG) { 932 System.out.println("in JPopupMenu.show " ); 933 } 934 setInvoker(invoker); 935 Frame newFrame = getFrame(invoker); 936 if (newFrame != frame) { 937 // Use the invoker's frame so that events 938 // are propagated properly 939 if (newFrame!=null) { 940 this.frame = newFrame; 941 if(popup != null) { 942 setVisible(false); 943 } 944 } 945 } 946 Point invokerOrigin; 947 if (invoker != null) { 948 invokerOrigin = invoker.getLocationOnScreen(); 949 950 // To avoid integer overflow 951 long lx, ly; 952 lx = ((long) invokerOrigin.x) + 953 ((long) x); 954 ly = ((long) invokerOrigin.y) + 955 ((long) y); 956 if(lx > Integer.MAX_VALUE) lx = Integer.MAX_VALUE; 957 if(lx < Integer.MIN_VALUE) lx = Integer.MIN_VALUE; 958 if(ly > Integer.MAX_VALUE) ly = Integer.MAX_VALUE; 959 if(ly < Integer.MIN_VALUE) ly = Integer.MIN_VALUE; 960 961 setLocation((int) lx, (int) ly); 962 } else { 963 setLocation(x, y); 964 } 965 setVisible(true); 966 } 967 968 /** 969 * Returns the popup menu which is at the root of the menu system 970 * for this popup menu. 971 * 972 * @return the topmost grandparent <code>JPopupMenu</code> 973 */ 974 JPopupMenu getRootPopupMenu() { 975 JPopupMenu mp = this; 976 while((mp!=null) && (mp.isPopupMenu()!=true) && 977 (mp.getInvoker() != null) && 978 (mp.getInvoker().getParent() != null) && 979 (mp.getInvoker().getParent() instanceof JPopupMenu) 980 ) { 981 mp = (JPopupMenu) mp.getInvoker().getParent(); 982 } 983 return mp; 984 } 985 986 /** 987 * Returns the component at the specified index. 988 * 989 * @param i the index of the component, where 0 is the first 990 * @return the <code>Component</code> at that index 991 * @deprecated replaced by {@link java.awt.Container#getComponent(int)} 992 */ 993 @Deprecated 994 public Component getComponentAtIndex(int i) { 995 return getComponent(i); 996 } 997 998 /** 999 * Returns the index of the specified component. 1000 * 1001 * @param c the <code>Component</code> to find 1002 * @return the index of the component, where 0 is the first; 1003 * or -1 if the component is not found 1004 */ 1005 public int getComponentIndex(Component c) { 1006 int ncomponents = this.getComponentCount(); 1007 Component[] component = this.getComponents(); 1008 for (int i = 0 ; i < ncomponents ; i++) { 1009 Component comp = component[i]; 1010 if (comp == c) 1011 return i; 1012 } 1013 return -1; 1014 } 1015 1016 /** 1017 * Sets the size of the Popup window using a <code>Dimension</code> object. 1018 * This is equivalent to <code>setPreferredSize(d)</code>. 1019 * 1020 * @param d the <code>Dimension</code> specifying the new size 1021 * of this component. 1022 * @beaninfo 1023 * description: The size of the popup menu 1024 */ 1025 public void setPopupSize(Dimension d) { 1026 Dimension oldSize = getPreferredSize(); 1027 1028 setPreferredSize(d); 1029 if (popup != null) { 1030 Dimension newSize = getPreferredSize(); 1031 1032 if (!oldSize.equals(newSize)) { 1033 popup = getPopup(); 1034 } 1035 } 1036 } 1037 1038 /** 1039 * Sets the size of the Popup window to the specified width and 1040 * height. This is equivalent to 1041 * <code>setPreferredSize(new Dimension(width, height))</code>. 1042 * 1043 * @param width the new width of the Popup in pixels 1044 * @param height the new height of the Popup in pixels 1045 * @beaninfo 1046 * description: The size of the popup menu 1047 */ 1048 public void setPopupSize(int width, int height) { 1049 setPopupSize(new Dimension(width, height)); 1050 } 1051 1052 /** 1053 * Sets the currently selected component, This will result 1054 * in a change to the selection model. 1055 * 1056 * @param sel the <code>Component</code> to select 1057 * @beaninfo 1058 * description: The selected component on the popup menu 1059 * expert: true 1060 * hidden: true 1061 */ 1062 public void setSelected(Component sel) { 1063 SingleSelectionModel model = getSelectionModel(); 1064 int index = getComponentIndex(sel); 1065 model.setSelectedIndex(index); 1066 } 1067 1068 /** 1069 * Checks whether the border should be painted. 1070 * 1071 * @return true if the border is painted, false otherwise 1072 * @see #setBorderPainted 1073 */ 1074 public boolean isBorderPainted() { 1075 return paintBorder; 1076 } 1077 1078 /** 1079 * Sets whether the border should be painted. 1080 * 1081 * @param b if true, the border is painted. 1082 * @see #isBorderPainted 1083 * @beaninfo 1084 * description: Is the border of the popup menu painted 1085 */ 1086 public void setBorderPainted(boolean b) { 1087 paintBorder = b; 1088 repaint(); 1089 } 1090 1091 /** 1092 * Paints the popup menu's border if the <code>borderPainted</code> 1093 * property is <code>true</code>. 1094 * @param g the <code>Graphics</code> object 1095 * 1096 * @see JComponent#paint 1097 * @see JComponent#setBorder 1098 */ 1099 protected void paintBorder(Graphics g) { 1100 if (isBorderPainted()) { 1101 super.paintBorder(g); 1102 } 1103 } 1104 1105 /** 1106 * Returns the margin, in pixels, between the popup menu's border and 1107 * its containees. 1108 * 1109 * @return an <code>Insets</code> object containing the margin values. 1110 */ 1111 public Insets getMargin() { 1112 if(margin == null) { 1113 return new Insets(0,0,0,0); 1114 } else { 1115 return margin; 1116 } 1117 } 1118 1119 1120 /** 1121 * Examines the list of menu items to determine whether 1122 * <code>popup</code> is a popup menu. 1123 * 1124 * @param popup a <code>JPopupMenu</code> 1125 * @return true if <code>popup</code> 1126 */ 1127 boolean isSubPopupMenu(JPopupMenu popup) { 1128 int ncomponents = this.getComponentCount(); 1129 Component[] component = this.getComponents(); 1130 for (int i = 0 ; i < ncomponents ; i++) { 1131 Component comp = component[i]; 1132 if (comp instanceof JMenu) { 1133 JMenu menu = (JMenu)comp; 1134 JPopupMenu subPopup = menu.getPopupMenu(); 1135 if (subPopup == popup) 1136 return true; 1137 if (subPopup.isSubPopupMenu(popup)) 1138 return true; 1139 } 1140 } 1141 return false; 1142 } 1143 1144 1145 private static Frame getFrame(Component c) { 1146 Component w = c; 1147 1148 while(!(w instanceof Frame) && (w!=null)) { 1149 w = w.getParent(); 1150 } 1151 return (Frame)w; 1152 } 1153 1154 1155 /** 1156 * Returns a string representation of this <code>JPopupMenu</code>. 1157 * This method 1158 * is intended to be used only for debugging purposes, and the 1159 * content and format of the returned string may vary between 1160 * implementations. The returned string may be empty but may not 1161 * be <code>null</code>. 1162 * 1163 * @return a string representation of this <code>JPopupMenu</code>. 1164 */ 1165 protected String paramString() { 1166 String labelString = (label != null ? 1167 label : ""); 1168 String paintBorderString = (paintBorder ? 1169 "true" : "false"); 1170 String marginString = (margin != null ? 1171 margin.toString() : ""); 1172 String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ? 1173 "true" : "false"); 1174 return super.paramString() + 1175 ",desiredLocationX=" + desiredLocationX + 1176 ",desiredLocationY=" + desiredLocationY + 1177 ",label=" + labelString + 1178 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString + 1179 ",margin=" + marginString + 1180 ",paintBorder=" + paintBorderString; 1181 } 1182 1183 ///////////////// 1184 // Accessibility support 1185 //////////////// 1186 1187 /** 1188 * Gets the AccessibleContext associated with this JPopupMenu. 1189 * For JPopupMenus, the AccessibleContext takes the form of an 1190 * AccessibleJPopupMenu. 1191 * A new AccessibleJPopupMenu instance is created if necessary. 1192 * 1193 * @return an AccessibleJPopupMenu that serves as the 1194 * AccessibleContext of this JPopupMenu 1195 */ 1196 public AccessibleContext getAccessibleContext() { 1197 if (accessibleContext == null) { 1198 accessibleContext = new AccessibleJPopupMenu(); 1199 } 1200 return accessibleContext; 1201 } 1202 1203 /** 1204 * This class implements accessibility support for the 1205 * <code>JPopupMenu</code> class. It provides an implementation of the 1206 * Java Accessibility API appropriate to popup menu user-interface 1207 * elements. 1208 */ 1209 @SuppressWarnings("serial") 1210 protected class AccessibleJPopupMenu extends AccessibleJComponent 1211 implements PropertyChangeListener { 1212 1213 /** 1214 * AccessibleJPopupMenu constructor 1215 * 1216 * @since 1.5 1217 */ 1218 protected AccessibleJPopupMenu() { 1219 JPopupMenu.this.addPropertyChangeListener(this); 1220 } 1221 1222 /** 1223 * Get the role of this object. 1224 * 1225 * @return an instance of AccessibleRole describing the role of 1226 * the object 1227 */ 1228 public AccessibleRole getAccessibleRole() { 1229 return AccessibleRole.POPUP_MENU; 1230 } 1231 1232 /** 1233 * This method gets called when a bound property is changed. 1234 * @param e A <code>PropertyChangeEvent</code> object describing 1235 * the event source and the property that has changed. Must not be null. 1236 * 1237 * @throws NullPointerException if the parameter is null. 1238 * @since 1.5 1239 */ 1240 public void propertyChange(PropertyChangeEvent e) { 1241 String propertyName = e.getPropertyName(); 1242 if (propertyName == "visible") { 1243 if (e.getOldValue() == Boolean.FALSE && 1244 e.getNewValue() == Boolean.TRUE) { 1245 handlePopupIsVisibleEvent(true); 1246 1247 } else if (e.getOldValue() == Boolean.TRUE && 1248 e.getNewValue() == Boolean.FALSE) { 1249 handlePopupIsVisibleEvent(false); 1250 } 1251 } 1252 } 1253 1254 /* 1255 * Handles popup "visible" PropertyChangeEvent 1256 */ 1257 private void handlePopupIsVisibleEvent(boolean visible) { 1258 if (visible) { 1259 // notify listeners that the popup became visible 1260 firePropertyChange(ACCESSIBLE_STATE_PROPERTY, 1261 null, AccessibleState.VISIBLE); 1262 // notify listeners that a popup list item is selected 1263 fireActiveDescendant(); 1264 } else { 1265 // notify listeners that the popup became hidden 1266 firePropertyChange(ACCESSIBLE_STATE_PROPERTY, 1267 AccessibleState.VISIBLE, null); 1268 } 1269 } 1270 1271 /* 1272 * Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners 1273 * on the popup menu invoker that a popup list item has been selected 1274 */ 1275 private void fireActiveDescendant() { 1276 if (JPopupMenu.this instanceof BasicComboPopup) { 1277 // get the popup list 1278 JList<?> popupList = ((BasicComboPopup)JPopupMenu.this).getList(); 1279 if (popupList == null) { 1280 return; 1281 } 1282 1283 // get the first selected item 1284 AccessibleContext ac = popupList.getAccessibleContext(); 1285 AccessibleSelection selection = ac.getAccessibleSelection(); 1286 if (selection == null) { 1287 return; 1288 } 1289 Accessible a = selection.getAccessibleSelection(0); 1290 if (a == null) { 1291 return; 1292 } 1293 AccessibleContext selectedItem = a.getAccessibleContext(); 1294 1295 // fire the event with the popup invoker as the source. 1296 if (selectedItem != null && invoker != null) { 1297 AccessibleContext invokerContext = invoker.getAccessibleContext(); 1298 if (invokerContext != null) { 1299 // Check invokerContext because Component.getAccessibleContext 1300 // returns null. Classes that extend Component are responsible 1301 // for returning a non-null AccessibleContext. 1302 invokerContext.firePropertyChange( 1303 ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, 1304 null, selectedItem); 1305 } 1306 } 1307 } 1308 } 1309 } // inner class AccessibleJPopupMenu 1310 1311 1312 //////////// 1313 // Serialization support. 1314 //////////// 1315 private void writeObject(ObjectOutputStream s) throws IOException { 1316 Vector<Object> values = new Vector<Object>(); 1317 1318 s.defaultWriteObject(); 1319 // Save the invoker, if its Serializable. 1320 if(invoker != null && invoker instanceof Serializable) { 1321 values.addElement("invoker"); 1322 values.addElement(invoker); 1323 } 1324 // Save the popup, if its Serializable. 1325 if(popup != null && popup instanceof Serializable) { 1326 values.addElement("popup"); 1327 values.addElement(popup); 1328 } 1329 s.writeObject(values); 1330 1331 if (getUIClassID().equals(uiClassID)) { 1332 byte count = JComponent.getWriteObjCounter(this); 1333 JComponent.setWriteObjCounter(this, --count); 1334 if (count == 0 && ui != null) { 1335 ui.installUI(this); 1336 } 1337 } 1338 } 1339 1340 // implements javax.swing.MenuElement 1341 private void readObject(ObjectInputStream s) 1342 throws IOException, ClassNotFoundException { 1343 s.defaultReadObject(); 1344 1345 Vector<?> values = (Vector)s.readObject(); 1346 int indexCounter = 0; 1347 int maxCounter = values.size(); 1348 1349 if(indexCounter < maxCounter && values.elementAt(indexCounter). 1350 equals("invoker")) { 1351 invoker = (Component)values.elementAt(++indexCounter); 1352 indexCounter++; 1353 } 1354 if(indexCounter < maxCounter && values.elementAt(indexCounter). 1355 equals("popup")) { 1356 popup = (Popup)values.elementAt(++indexCounter); 1357 indexCounter++; 1358 } 1359 } 1360 1361 1362 /** 1363 * This method is required to conform to the 1364 * <code>MenuElement</code> interface, but it not implemented. 1365 * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager) 1366 */ 1367 public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {} 1368 1369 /** 1370 * Processes a key event forwarded from the 1371 * <code>MenuSelectionManager</code> and changes the menu selection, 1372 * if necessary, by using <code>MenuSelectionManager</code>'s API. 1373 * <p> 1374 * Note: you do not have to forward the event to sub-components. 1375 * This is done automatically by the <code>MenuSelectionManager</code>. 1376 * 1377 * @param e a <code>KeyEvent</code> 1378 * @param path the <code>MenuElement</code> path array 1379 * @param manager the <code>MenuSelectionManager</code> 1380 */ 1381 public void processKeyEvent(KeyEvent e, MenuElement path[], 1382 MenuSelectionManager manager) { 1383 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(), 1384 e.getWhen(), e.getModifiers(), 1385 e.getKeyCode(), e.getKeyChar(), 1386 path, manager); 1387 processMenuKeyEvent(mke); 1388 1389 if (mke.isConsumed()) { 1390 e.consume(); 1391 } 1392 } 1393 1394 /** 1395 * Handles a keystroke in a menu. 1396 * 1397 * @param e a <code>MenuKeyEvent</code> object 1398 * @since 1.5 1399 */ 1400 private void processMenuKeyEvent(MenuKeyEvent e) { 1401 switch (e.getID()) { 1402 case KeyEvent.KEY_PRESSED: 1403 fireMenuKeyPressed(e); break; 1404 case KeyEvent.KEY_RELEASED: 1405 fireMenuKeyReleased(e); break; 1406 case KeyEvent.KEY_TYPED: 1407 fireMenuKeyTyped(e); break; 1408 default: 1409 break; 1410 } 1411 } 1412 1413 /** 1414 * Notifies all listeners that have registered interest for 1415 * notification on this event type. 1416 * 1417 * @param event a <code>MenuKeyEvent</code> 1418 * @see EventListenerList 1419 */ 1420 private void fireMenuKeyPressed(MenuKeyEvent event) { 1421 Object[] listeners = listenerList.getListenerList(); 1422 for (int i = listeners.length-2; i>=0; i-=2) { 1423 if (listeners[i]==MenuKeyListener.class) { 1424 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event); 1425 } 1426 } 1427 } 1428 1429 /** 1430 * Notifies all listeners that have registered interest for 1431 * notification on this event type. 1432 * 1433 * @param event a <code>MenuKeyEvent</code> 1434 * @see EventListenerList 1435 */ 1436 private void fireMenuKeyReleased(MenuKeyEvent event) { 1437 Object[] listeners = listenerList.getListenerList(); 1438 for (int i = listeners.length-2; i>=0; i-=2) { 1439 if (listeners[i]==MenuKeyListener.class) { 1440 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event); 1441 } 1442 } 1443 } 1444 1445 /** 1446 * Notifies all listeners that have registered interest for 1447 * notification on this event type. 1448 * 1449 * @param event a <code>MenuKeyEvent</code> 1450 * @see EventListenerList 1451 */ 1452 private void fireMenuKeyTyped(MenuKeyEvent event) { 1453 Object[] listeners = listenerList.getListenerList(); 1454 for (int i = listeners.length-2; i>=0; i-=2) { 1455 if (listeners[i]==MenuKeyListener.class) { 1456 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event); 1457 } 1458 } 1459 } 1460 1461 /** 1462 * Messaged when the menubar selection changes to activate or 1463 * deactivate this menu. This implements the 1464 * <code>javax.swing.MenuElement</code> interface. 1465 * Overrides <code>MenuElement.menuSelectionChanged</code>. 1466 * 1467 * @param isIncluded true if this menu is active, false if 1468 * it is not 1469 * @see MenuElement#menuSelectionChanged(boolean) 1470 */ 1471 public void menuSelectionChanged(boolean isIncluded) { 1472 if (DEBUG) { 1473 System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded); 1474 } 1475 if(invoker instanceof JMenu) { 1476 JMenu m = (JMenu) invoker; 1477 if(isIncluded) 1478 m.setPopupMenuVisible(true); 1479 else 1480 m.setPopupMenuVisible(false); 1481 } 1482 if (isPopupMenu() && !isIncluded) 1483 setVisible(false); 1484 } 1485 1486 /** 1487 * Returns an array of <code>MenuElement</code>s containing the submenu 1488 * for this menu component. It will only return items conforming to 1489 * the <code>JMenuElement</code> interface. 1490 * If popup menu is <code>null</code> returns 1491 * an empty array. This method is required to conform to the 1492 * <code>MenuElement</code> interface. 1493 * 1494 * @return an array of <code>MenuElement</code> objects 1495 * @see MenuElement#getSubElements 1496 */ 1497 public MenuElement[] getSubElements() { 1498 MenuElement result[]; 1499 Vector<MenuElement> tmp = new Vector<MenuElement>(); 1500 int c = getComponentCount(); 1501 int i; 1502 Component m; 1503 1504 for(i=0 ; i < c ; i++) { 1505 m = getComponent(i); 1506 if(m instanceof MenuElement) 1507 tmp.addElement((MenuElement) m); 1508 } 1509 1510 result = new MenuElement[tmp.size()]; 1511 for(i=0,c=tmp.size() ; i < c ; i++) 1512 result[i] = tmp.elementAt(i); 1513 return result; 1514 } 1515 1516 /** 1517 * Returns this <code>JPopupMenu</code> component. 1518 * @return this <code>JPopupMenu</code> object 1519 * @see MenuElement#getComponent 1520 */ 1521 public Component getComponent() { 1522 return this; 1523 } 1524 1525 1526 /** 1527 * A popup menu-specific separator. 1528 */ 1529 @SuppressWarnings("serial") 1530 static public class Separator extends JSeparator 1531 { 1532 public Separator( ) 1533 { 1534 super( JSeparator.HORIZONTAL ); 1535 } 1536 1537 /** 1538 * Returns the name of the L&F class that renders this component. 1539 * 1540 * @return the string "PopupMenuSeparatorUI" 1541 * @see JComponent#getUIClassID 1542 * @see UIDefaults#getUI 1543 */ 1544 public String getUIClassID() 1545 { 1546 return "PopupMenuSeparatorUI"; 1547 1548 } 1549 } 1550 1551 /** 1552 * Returns true if the <code>MouseEvent</code> is considered a popup trigger 1553 * by the <code>JPopupMenu</code>'s currently installed UI. 1554 * 1555 * @return true if the mouse event is a popup trigger 1556 * @since 1.3 1557 */ 1558 public boolean isPopupTrigger(MouseEvent e) { 1559 return getUI().isPopupTrigger(e); 1560 } 1561 }