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