1 /* 2 * Copyright (c) 1997, 2008, 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.MEDIUM_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 * 857 * @param x the x coordinate of the popup's new position 858 * in the screen's coordinate space 859 * @param y the y coordinate of the popup's new position 860 * in the screen's coordinate space 861 * @beaninfo 862 * description: The location of the popup menu. 863 */ 864 public void setLocation(int x, int y) { 865 int oldX = desiredLocationX; 866 int oldY = desiredLocationY; 867 868 desiredLocationX = x; 869 desiredLocationY = y; 870 if(popup != null && (x != oldX || y != oldY)) { 871 popup = getPopup(); 872 } 873 } 874 875 /** 876 * Returns true if the popup menu is a standalone popup menu 877 * rather than the submenu of a <code>JMenu</code>. 878 * 879 * @return true if this menu is a standalone popup menu, otherwise false 880 */ 881 private boolean isPopupMenu() { 882 return ((invoker != null) && !(invoker instanceof JMenu)); 883 } 884 885 /** 886 * Returns the component which is the 'invoker' of this 887 * popup menu. 888 * 889 * @return the <code>Component</code> in which the popup menu is displayed 890 */ 891 public Component getInvoker() { 892 return this.invoker; 893 } 894 895 /** 896 * Sets the invoker of this popup menu -- the component in which 897 * the popup menu menu is to be displayed. 898 * 899 * @param invoker the <code>Component</code> in which the popup 900 * menu is displayed 901 * @beaninfo 902 * description: The invoking component for the popup menu 903 * expert: true 904 */ 905 public void setInvoker(Component invoker) { 906 Component oldInvoker = this.invoker; 907 this.invoker = invoker; 908 if ((oldInvoker != this.invoker) && (ui != null)) { 909 ui.uninstallUI(this); 910 ui.installUI(this); 911 } 912 invalidate(); 913 } 914 915 /** 916 * Displays the popup menu at the position x,y in the coordinate 917 * space of the component invoker. 918 * 919 * @param invoker the component in whose space the popup menu is to appear 920 * @param x the x coordinate in invoker's coordinate space at which 921 * the popup menu is to be displayed 922 * @param y the y coordinate in invoker's coordinate space at which 923 * the popup menu is to be displayed 924 */ 925 public void show(Component invoker, int x, int y) { 926 if (DEBUG) { 927 System.out.println("in JPopupMenu.show " ); 928 } 929 setInvoker(invoker); 930 Frame newFrame = getFrame(invoker); 931 if (newFrame != frame) { 932 // Use the invoker's frame so that events 933 // are propagated properly 934 if (newFrame!=null) { 935 this.frame = newFrame; 936 if(popup != null) { 937 setVisible(false); 938 } 939 } 940 } 941 Point invokerOrigin; 942 if (invoker != null) { 943 invokerOrigin = invoker.getLocationOnScreen(); 944 945 // To avoid integer overflow 946 long lx, ly; 947 lx = ((long) invokerOrigin.x) + 948 ((long) x); 949 ly = ((long) invokerOrigin.y) + 950 ((long) y); 951 if(lx > Integer.MAX_VALUE) lx = Integer.MAX_VALUE; 952 if(lx < Integer.MIN_VALUE) lx = Integer.MIN_VALUE; 953 if(ly > Integer.MAX_VALUE) ly = Integer.MAX_VALUE; 954 if(ly < Integer.MIN_VALUE) ly = Integer.MIN_VALUE; 955 956 setLocation((int) lx, (int) ly); 957 } else { 958 setLocation(x, y); 959 } 960 setVisible(true); 961 } 962 963 /** 964 * Returns the popup menu which is at the root of the menu system 965 * for this popup menu. 966 * 967 * @return the topmost grandparent <code>JPopupMenu</code> 968 */ 969 JPopupMenu getRootPopupMenu() { 970 JPopupMenu mp = this; 971 while((mp!=null) && (mp.isPopupMenu()!=true) && 972 (mp.getInvoker() != null) && 973 (mp.getInvoker().getParent() != null) && 974 (mp.getInvoker().getParent() instanceof JPopupMenu) 975 ) { 976 mp = (JPopupMenu) mp.getInvoker().getParent(); 977 } 978 return mp; 979 } 980 981 /** 982 * Returns the component at the specified index. 983 * 984 * @param i the index of the component, where 0 is the first 985 * @return the <code>Component</code> at that index 986 * @deprecated replaced by {@link java.awt.Container#getComponent(int)} 987 */ 988 @Deprecated 989 public Component getComponentAtIndex(int i) { 990 return getComponent(i); 991 } 992 993 /** 994 * Returns the index of the specified component. 995 * 996 * @param c the <code>Component</code> to find 997 * @return the index of the component, where 0 is the first; 998 * or -1 if the component is not found 999 */ 1000 public int getComponentIndex(Component c) { 1001 int ncomponents = this.getComponentCount(); 1002 Component[] component = this.getComponents(); 1003 for (int i = 0 ; i < ncomponents ; i++) { 1004 Component comp = component[i]; 1005 if (comp == c) 1006 return i; 1007 } 1008 return -1; 1009 } 1010 1011 /** 1012 * Sets the size of the Popup window using a <code>Dimension</code> object. 1013 * This is equivalent to <code>setPreferredSize(d)</code>. 1014 * 1015 * @param d the <code>Dimension</code> specifying the new size 1016 * of this component. 1017 * @beaninfo 1018 * description: The size of the popup menu 1019 */ 1020 public void setPopupSize(Dimension d) { 1021 Dimension oldSize = getPreferredSize(); 1022 1023 setPreferredSize(d); 1024 if (popup != null) { 1025 Dimension newSize = getPreferredSize(); 1026 1027 if (!oldSize.equals(newSize)) { 1028 popup = getPopup(); 1029 } 1030 } 1031 } 1032 1033 /** 1034 * Sets the size of the Popup window to the specified width and 1035 * height. This is equivalent to 1036 * <code>setPreferredSize(new Dimension(width, height))</code>. 1037 * 1038 * @param width the new width of the Popup in pixels 1039 * @param height the new height of the Popup in pixels 1040 * @beaninfo 1041 * description: The size of the popup menu 1042 */ 1043 public void setPopupSize(int width, int height) { 1044 setPopupSize(new Dimension(width, height)); 1045 } 1046 1047 /** 1048 * Sets the currently selected component, This will result 1049 * in a change to the selection model. 1050 * 1051 * @param sel the <code>Component</code> to select 1052 * @beaninfo 1053 * description: The selected component on the popup menu 1054 * expert: true 1055 * hidden: true 1056 */ 1057 public void setSelected(Component sel) { 1058 SingleSelectionModel model = getSelectionModel(); 1059 int index = getComponentIndex(sel); 1060 model.setSelectedIndex(index); 1061 } 1062 1063 /** 1064 * Checks whether the border should be painted. 1065 * 1066 * @return true if the border is painted, false otherwise 1067 * @see #setBorderPainted 1068 */ 1069 public boolean isBorderPainted() { 1070 return paintBorder; 1071 } 1072 1073 /** 1074 * Sets whether the border should be painted. 1075 * 1076 * @param b if true, the border is painted. 1077 * @see #isBorderPainted 1078 * @beaninfo 1079 * description: Is the border of the popup menu painted 1080 */ 1081 public void setBorderPainted(boolean b) { 1082 paintBorder = b; 1083 repaint(); 1084 } 1085 1086 /** 1087 * Paints the popup menu's border if the <code>borderPainted</code> 1088 * property is <code>true</code>. 1089 * @param g the <code>Graphics</code> object 1090 * 1091 * @see JComponent#paint 1092 * @see JComponent#setBorder 1093 */ 1094 protected void paintBorder(Graphics g) { 1095 if (isBorderPainted()) { 1096 super.paintBorder(g); 1097 } 1098 } 1099 1100 /** 1101 * Returns the margin, in pixels, between the popup menu's border and 1102 * its containees. 1103 * 1104 * @return an <code>Insets</code> object containing the margin values. 1105 */ 1106 public Insets getMargin() { 1107 if(margin == null) { 1108 return new Insets(0,0,0,0); 1109 } else { 1110 return margin; 1111 } 1112 } 1113 1114 1115 /** 1116 * Examines the list of menu items to determine whether 1117 * <code>popup</code> is a popup menu. 1118 * 1119 * @param popup a <code>JPopupMenu</code> 1120 * @return true if <code>popup</code> 1121 */ 1122 boolean isSubPopupMenu(JPopupMenu popup) { 1123 int ncomponents = this.getComponentCount(); 1124 Component[] component = this.getComponents(); 1125 for (int i = 0 ; i < ncomponents ; i++) { 1126 Component comp = component[i]; 1127 if (comp instanceof JMenu) { 1128 JMenu menu = (JMenu)comp; 1129 JPopupMenu subPopup = menu.getPopupMenu(); 1130 if (subPopup == popup) 1131 return true; 1132 if (subPopup.isSubPopupMenu(popup)) 1133 return true; 1134 } 1135 } 1136 return false; 1137 } 1138 1139 1140 private static Frame getFrame(Component c) { 1141 Component w = c; 1142 1143 while(!(w instanceof Frame) && (w!=null)) { 1144 w = w.getParent(); 1145 } 1146 return (Frame)w; 1147 } 1148 1149 1150 /** 1151 * Returns a string representation of this <code>JPopupMenu</code>. 1152 * This method 1153 * is intended to be used only for debugging purposes, and the 1154 * content and format of the returned string may vary between 1155 * implementations. The returned string may be empty but may not 1156 * be <code>null</code>. 1157 * 1158 * @return a string representation of this <code>JPopupMenu</code>. 1159 */ 1160 protected String paramString() { 1161 String labelString = (label != null ? 1162 label : ""); 1163 String paintBorderString = (paintBorder ? 1164 "true" : "false"); 1165 String marginString = (margin != null ? 1166 margin.toString() : ""); 1167 String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ? 1168 "true" : "false"); 1169 return super.paramString() + 1170 ",desiredLocationX=" + desiredLocationX + 1171 ",desiredLocationY=" + desiredLocationY + 1172 ",label=" + labelString + 1173 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString + 1174 ",margin=" + marginString + 1175 ",paintBorder=" + paintBorderString; 1176 } 1177 1178 ///////////////// 1179 // Accessibility support 1180 //////////////// 1181 1182 /** 1183 * Gets the AccessibleContext associated with this JPopupMenu. 1184 * For JPopupMenus, the AccessibleContext takes the form of an 1185 * AccessibleJPopupMenu. 1186 * A new AccessibleJPopupMenu instance is created if necessary. 1187 * 1188 * @return an AccessibleJPopupMenu that serves as the 1189 * AccessibleContext of this JPopupMenu 1190 */ 1191 public AccessibleContext getAccessibleContext() { 1192 if (accessibleContext == null) { 1193 accessibleContext = new AccessibleJPopupMenu(); 1194 } 1195 return accessibleContext; 1196 } 1197 1198 /** 1199 * This class implements accessibility support for the 1200 * <code>JPopupMenu</code> class. It provides an implementation of the 1201 * Java Accessibility API appropriate to popup menu user-interface 1202 * elements. 1203 */ 1204 @SuppressWarnings("serial") 1205 protected class AccessibleJPopupMenu extends AccessibleJComponent 1206 implements PropertyChangeListener { 1207 1208 /** 1209 * AccessibleJPopupMenu constructor 1210 * 1211 * @since 1.5 1212 */ 1213 protected AccessibleJPopupMenu() { 1214 JPopupMenu.this.addPropertyChangeListener(this); 1215 } 1216 1217 /** 1218 * Get the role of this object. 1219 * 1220 * @return an instance of AccessibleRole describing the role of 1221 * the object 1222 */ 1223 public AccessibleRole getAccessibleRole() { 1224 return AccessibleRole.POPUP_MENU; 1225 } 1226 1227 /** 1228 * This method gets called when a bound property is changed. 1229 * @param e A <code>PropertyChangeEvent</code> object describing 1230 * the event source and the property that has changed. Must not be null. 1231 * 1232 * @throws NullPointerException if the parameter is null. 1233 * @since 1.5 1234 */ 1235 public void propertyChange(PropertyChangeEvent e) { 1236 String propertyName = e.getPropertyName(); 1237 if (propertyName == "visible") { 1238 if (e.getOldValue() == Boolean.FALSE && 1239 e.getNewValue() == Boolean.TRUE) { 1240 handlePopupIsVisibleEvent(true); 1241 1242 } else if (e.getOldValue() == Boolean.TRUE && 1243 e.getNewValue() == Boolean.FALSE) { 1244 handlePopupIsVisibleEvent(false); 1245 } 1246 } 1247 } 1248 1249 /* 1250 * Handles popup "visible" PropertyChangeEvent 1251 */ 1252 private void handlePopupIsVisibleEvent(boolean visible) { 1253 if (visible) { 1254 // notify listeners that the popup became visible 1255 firePropertyChange(ACCESSIBLE_STATE_PROPERTY, 1256 null, AccessibleState.VISIBLE); 1257 // notify listeners that a popup list item is selected 1258 fireActiveDescendant(); 1259 } else { 1260 // notify listeners that the popup became hidden 1261 firePropertyChange(ACCESSIBLE_STATE_PROPERTY, 1262 AccessibleState.VISIBLE, null); 1263 } 1264 } 1265 1266 /* 1267 * Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners 1268 * on the popup menu invoker that a popup list item has been selected 1269 */ 1270 private void fireActiveDescendant() { 1271 if (JPopupMenu.this instanceof BasicComboPopup) { 1272 // get the popup list 1273 JList<?> popupList = ((BasicComboPopup)JPopupMenu.this).getList(); 1274 if (popupList == null) { 1275 return; 1276 } 1277 1278 // get the first selected item 1279 AccessibleContext ac = popupList.getAccessibleContext(); 1280 AccessibleSelection selection = ac.getAccessibleSelection(); 1281 if (selection == null) { 1282 return; 1283 } 1284 Accessible a = selection.getAccessibleSelection(0); 1285 if (a == null) { 1286 return; 1287 } 1288 AccessibleContext selectedItem = a.getAccessibleContext(); 1289 1290 // fire the event with the popup invoker as the source. 1291 if (selectedItem != null && invoker != null) { 1292 AccessibleContext invokerContext = invoker.getAccessibleContext(); 1293 if (invokerContext != null) { 1294 // Check invokerContext because Component.getAccessibleContext 1295 // returns null. Classes that extend Component are responsible 1296 // for returning a non-null AccessibleContext. 1297 invokerContext.firePropertyChange( 1298 ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, 1299 null, selectedItem); 1300 } 1301 } 1302 } 1303 } 1304 } // inner class AccessibleJPopupMenu 1305 1306 1307 //////////// 1308 // Serialization support. 1309 //////////// 1310 private void writeObject(ObjectOutputStream s) throws IOException { 1311 Vector<Object> values = new Vector<Object>(); 1312 1313 s.defaultWriteObject(); 1314 // Save the invoker, if its Serializable. 1315 if(invoker != null && invoker instanceof Serializable) { 1316 values.addElement("invoker"); 1317 values.addElement(invoker); 1318 } 1319 // Save the popup, if its Serializable. 1320 if(popup != null && popup instanceof Serializable) { 1321 values.addElement("popup"); 1322 values.addElement(popup); 1323 } 1324 s.writeObject(values); 1325 1326 if (getUIClassID().equals(uiClassID)) { 1327 byte count = JComponent.getWriteObjCounter(this); 1328 JComponent.setWriteObjCounter(this, --count); 1329 if (count == 0 && ui != null) { 1330 ui.installUI(this); 1331 } 1332 } 1333 } 1334 1335 // implements javax.swing.MenuElement 1336 private void readObject(ObjectInputStream s) 1337 throws IOException, ClassNotFoundException { 1338 s.defaultReadObject(); 1339 1340 Vector<?> values = (Vector)s.readObject(); 1341 int indexCounter = 0; 1342 int maxCounter = values.size(); 1343 1344 if(indexCounter < maxCounter && values.elementAt(indexCounter). 1345 equals("invoker")) { 1346 invoker = (Component)values.elementAt(++indexCounter); 1347 indexCounter++; 1348 } 1349 if(indexCounter < maxCounter && values.elementAt(indexCounter). 1350 equals("popup")) { 1351 popup = (Popup)values.elementAt(++indexCounter); 1352 indexCounter++; 1353 } 1354 } 1355 1356 1357 /** 1358 * This method is required to conform to the 1359 * <code>MenuElement</code> interface, but it not implemented. 1360 * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager) 1361 */ 1362 public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {} 1363 1364 /** 1365 * Processes a key event forwarded from the 1366 * <code>MenuSelectionManager</code> and changes the menu selection, 1367 * if necessary, by using <code>MenuSelectionManager</code>'s API. 1368 * <p> 1369 * Note: you do not have to forward the event to sub-components. 1370 * This is done automatically by the <code>MenuSelectionManager</code>. 1371 * 1372 * @param e a <code>KeyEvent</code> 1373 * @param path the <code>MenuElement</code> path array 1374 * @param manager the <code>MenuSelectionManager</code> 1375 */ 1376 public void processKeyEvent(KeyEvent e, MenuElement path[], 1377 MenuSelectionManager manager) { 1378 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(), 1379 e.getWhen(), e.getModifiers(), 1380 e.getKeyCode(), e.getKeyChar(), 1381 path, manager); 1382 processMenuKeyEvent(mke); 1383 1384 if (mke.isConsumed()) { 1385 e.consume(); 1386 } 1387 } 1388 1389 /** 1390 * Handles a keystroke in a menu. 1391 * 1392 * @param e a <code>MenuKeyEvent</code> object 1393 * @since 1.5 1394 */ 1395 private void processMenuKeyEvent(MenuKeyEvent e) { 1396 switch (e.getID()) { 1397 case KeyEvent.KEY_PRESSED: 1398 fireMenuKeyPressed(e); break; 1399 case KeyEvent.KEY_RELEASED: 1400 fireMenuKeyReleased(e); break; 1401 case KeyEvent.KEY_TYPED: 1402 fireMenuKeyTyped(e); break; 1403 default: 1404 break; 1405 } 1406 } 1407 1408 /** 1409 * Notifies all listeners that have registered interest for 1410 * notification on this event type. 1411 * 1412 * @param event a <code>MenuKeyEvent</code> 1413 * @see EventListenerList 1414 */ 1415 private void fireMenuKeyPressed(MenuKeyEvent event) { 1416 Object[] listeners = listenerList.getListenerList(); 1417 for (int i = listeners.length-2; i>=0; i-=2) { 1418 if (listeners[i]==MenuKeyListener.class) { 1419 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event); 1420 } 1421 } 1422 } 1423 1424 /** 1425 * Notifies all listeners that have registered interest for 1426 * notification on this event type. 1427 * 1428 * @param event a <code>MenuKeyEvent</code> 1429 * @see EventListenerList 1430 */ 1431 private void fireMenuKeyReleased(MenuKeyEvent event) { 1432 Object[] listeners = listenerList.getListenerList(); 1433 for (int i = listeners.length-2; i>=0; i-=2) { 1434 if (listeners[i]==MenuKeyListener.class) { 1435 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event); 1436 } 1437 } 1438 } 1439 1440 /** 1441 * Notifies all listeners that have registered interest for 1442 * notification on this event type. 1443 * 1444 * @param event a <code>MenuKeyEvent</code> 1445 * @see EventListenerList 1446 */ 1447 private void fireMenuKeyTyped(MenuKeyEvent event) { 1448 Object[] listeners = listenerList.getListenerList(); 1449 for (int i = listeners.length-2; i>=0; i-=2) { 1450 if (listeners[i]==MenuKeyListener.class) { 1451 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event); 1452 } 1453 } 1454 } 1455 1456 /** 1457 * Messaged when the menubar selection changes to activate or 1458 * deactivate this menu. This implements the 1459 * <code>javax.swing.MenuElement</code> interface. 1460 * Overrides <code>MenuElement.menuSelectionChanged</code>. 1461 * 1462 * @param isIncluded true if this menu is active, false if 1463 * it is not 1464 * @see MenuElement#menuSelectionChanged(boolean) 1465 */ 1466 public void menuSelectionChanged(boolean isIncluded) { 1467 if (DEBUG) { 1468 System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded); 1469 } 1470 if(invoker instanceof JMenu) { 1471 JMenu m = (JMenu) invoker; 1472 if(isIncluded) 1473 m.setPopupMenuVisible(true); 1474 else 1475 m.setPopupMenuVisible(false); 1476 } 1477 if (isPopupMenu() && !isIncluded) 1478 setVisible(false); 1479 } 1480 1481 /** 1482 * Returns an array of <code>MenuElement</code>s containing the submenu 1483 * for this menu component. It will only return items conforming to 1484 * the <code>JMenuElement</code> interface. 1485 * If popup menu is <code>null</code> returns 1486 * an empty array. This method is required to conform to the 1487 * <code>MenuElement</code> interface. 1488 * 1489 * @return an array of <code>MenuElement</code> objects 1490 * @see MenuElement#getSubElements 1491 */ 1492 public MenuElement[] getSubElements() { 1493 MenuElement result[]; 1494 Vector<MenuElement> tmp = new Vector<MenuElement>(); 1495 int c = getComponentCount(); 1496 int i; 1497 Component m; 1498 1499 for(i=0 ; i < c ; i++) { 1500 m = getComponent(i); 1501 if(m instanceof MenuElement) 1502 tmp.addElement((MenuElement) m); 1503 } 1504 1505 result = new MenuElement[tmp.size()]; 1506 for(i=0,c=tmp.size() ; i < c ; i++) 1507 result[i] = tmp.elementAt(i); 1508 return result; 1509 } 1510 1511 /** 1512 * Returns this <code>JPopupMenu</code> component. 1513 * @return this <code>JPopupMenu</code> object 1514 * @see MenuElement#getComponent 1515 */ 1516 public Component getComponent() { 1517 return this; 1518 } 1519 1520 1521 /** 1522 * A popup menu-specific separator. 1523 */ 1524 @SuppressWarnings("serial") 1525 static public class Separator extends JSeparator 1526 { 1527 public Separator( ) 1528 { 1529 super( JSeparator.HORIZONTAL ); 1530 } 1531 1532 /** 1533 * Returns the name of the L&F class that renders this component. 1534 * 1535 * @return the string "PopupMenuSeparatorUI" 1536 * @see JComponent#getUIClassID 1537 * @see UIDefaults#getUI 1538 */ 1539 public String getUIClassID() 1540 { 1541 return "PopupMenuSeparatorUI"; 1542 1543 } 1544 } 1545 1546 /** 1547 * Returns true if the <code>MouseEvent</code> is considered a popup trigger 1548 * by the <code>JPopupMenu</code>'s currently installed UI. 1549 * 1550 * @return true if the mouse event is a popup trigger 1551 * @since 1.3 1552 */ 1553 public boolean isPopupTrigger(MouseEvent e) { 1554 return getUI().isPopupTrigger(e); 1555 } 1556 }