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