1 /* 2 * Copyright (c) 1997, 2013, 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="http://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 Rectangle scrBounds; 329 GraphicsConfiguration gc = getCurrentGraphicsConfiguration(popupLocation); 330 Toolkit toolkit = Toolkit.getDefaultToolkit(); 331 if(gc != null) { 332 // If we have GraphicsConfiguration use it to get screen bounds 333 scrBounds = gc.getBounds(); 334 } else { 335 // If we don't have GraphicsConfiguration use primary screen 336 scrBounds = new Rectangle(toolkit.getScreenSize()); 337 } 338 339 // Calculate the screen size that popup should fit 340 Dimension popupSize = JPopupMenu.this.getPreferredSize(); 341 long popupRightX = (long)popupLocation.x + (long)popupSize.width; 342 long popupBottomY = (long)popupLocation.y + (long)popupSize.height; 343 int scrWidth = scrBounds.width; 344 int scrHeight = scrBounds.height; 345 346 if (!canPopupOverlapTaskBar()) { 347 // Insets include the task bar. Take them into account. 348 Insets scrInsets = toolkit.getScreenInsets(gc); 349 scrBounds.x += scrInsets.left; 350 scrBounds.y += scrInsets.top; 351 scrWidth -= scrInsets.left + scrInsets.right; 352 scrHeight -= scrInsets.top + scrInsets.bottom; 353 } 354 int scrRightX = scrBounds.x + scrWidth; 355 int scrBottomY = scrBounds.y + scrHeight; 356 357 // Ensure that popup menu fits the screen 358 if (popupRightX > (long) scrRightX) { 359 popupLocation.x = scrRightX - popupSize.width; 360 } 361 362 if (popupBottomY > (long) scrBottomY) { 363 popupLocation.y = scrBottomY - popupSize.height; 364 } 365 366 if (popupLocation.x < scrBounds.x) { 367 popupLocation.x = scrBounds.x; 368 } 369 370 if (popupLocation.y < scrBounds.y) { 371 popupLocation.y = scrBounds.y; 372 } 373 374 return popupLocation; 375 } 376 377 /** 378 * Tries to find GraphicsConfiguration 379 * that contains the mouse cursor position. 380 * Can return null. 381 */ 382 private GraphicsConfiguration getCurrentGraphicsConfiguration( 383 Point popupLocation) { 384 GraphicsConfiguration gc = null; 385 GraphicsEnvironment ge = 386 GraphicsEnvironment.getLocalGraphicsEnvironment(); 387 GraphicsDevice[] gd = ge.getScreenDevices(); 388 for(int i = 0; i < gd.length; i++) { 389 if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) { 390 GraphicsConfiguration dgc = 391 gd[i].getDefaultConfiguration(); 392 if(dgc.getBounds().contains(popupLocation)) { 393 gc = dgc; 394 break; 395 } 396 } 397 } 398 // If not found and we have invoker, ask invoker about his gc 399 if(gc == null && getInvoker() != null) { 400 gc = getInvoker().getGraphicsConfiguration(); 401 } 402 return gc; 403 } 404 405 /** 406 * Returns whether popup is allowed to be shown above the task bar. 407 */ 408 static boolean canPopupOverlapTaskBar() { 409 boolean result = true; 410 411 Toolkit tk = Toolkit.getDefaultToolkit(); 412 if (tk instanceof SunToolkit) { 413 result = ((SunToolkit)tk).canPopupOverlapTaskBar(); 414 } 415 416 return result; 417 } 418 419 /** 420 * Factory method which creates the <code>JMenuItem</code> for 421 * <code>Actions</code> added to the <code>JPopupMenu</code>. 422 * 423 * @param a the <code>Action</code> for the menu item to be added 424 * @return the new menu item 425 * @see Action 426 * 427 * @since 1.3 428 */ 429 protected JMenuItem createActionComponent(Action a) { 430 JMenuItem mi = new JMenuItem() { 431 protected PropertyChangeListener createActionPropertyChangeListener(Action a) { 432 PropertyChangeListener pcl = createActionChangeListener(this); 433 if (pcl == null) { 434 pcl = super.createActionPropertyChangeListener(a); 435 } 436 return pcl; 437 } 438 }; 439 mi.setHorizontalTextPosition(JButton.TRAILING); 440 mi.setVerticalTextPosition(JButton.CENTER); 441 return mi; 442 } 443 444 /** 445 * Returns a properly configured <code>PropertyChangeListener</code> 446 * which updates the control as changes to the <code>Action</code> occur. 447 * 448 * @param b the menu item for which to create a listener 449 * @return a properly configured {@code PropertyChangeListener} 450 */ 451 protected PropertyChangeListener createActionChangeListener(JMenuItem b) { 452 return b.createActionPropertyChangeListener0(b.getAction()); 453 } 454 455 /** 456 * Removes the component at the specified index from this popup menu. 457 * 458 * @param pos the position of the item to be removed 459 * @exception IllegalArgumentException if the value of 460 * <code>pos</code> < 0, or if the value of 461 * <code>pos</code> is greater than the 462 * number of items 463 */ 464 public void remove(int pos) { 465 if (pos < 0) { 466 throw new IllegalArgumentException("index less than zero."); 467 } 468 if (pos > getComponentCount() -1) { 469 throw new IllegalArgumentException("index greater than the number of items."); 470 } 471 super.remove(pos); 472 } 473 474 /** 475 * Sets the value of the <code>lightWeightPopupEnabled</code> property, 476 * which by default is <code>true</code>. 477 * By default, when a look and feel displays a popup, 478 * it can choose to 479 * use a lightweight (all-Java) popup. 480 * Lightweight popup windows are more efficient than heavyweight 481 * (native peer) windows, 482 * but lightweight and heavyweight components do not mix well in a GUI. 483 * If your application mixes lightweight and heavyweight components, 484 * you should disable lightweight popups. 485 * Some look and feels might always use heavyweight popups, 486 * no matter what the value of this property. 487 * 488 * @param aFlag <code>false</code> to disable lightweight popups 489 * 490 * @see #isLightWeightPopupEnabled 491 */ 492 @BeanProperty(bound = false, expert = true, description 493 = "Determines whether lightweight popups are used when possible") 494 public void setLightWeightPopupEnabled(boolean aFlag) { 495 // NOTE: this use to set the flag on a shared JPopupMenu, which meant 496 // this effected ALL JPopupMenus. 497 lightWeightPopup = aFlag; 498 } 499 500 /** 501 * Gets the <code>lightWeightPopupEnabled</code> property. 502 * 503 * @return the value of the <code>lightWeightPopupEnabled</code> property 504 * @see #setLightWeightPopupEnabled 505 */ 506 public boolean isLightWeightPopupEnabled() { 507 return lightWeightPopup; 508 } 509 510 /** 511 * Returns the popup menu's label 512 * 513 * @return a string containing the popup menu's label 514 * @see #setLabel 515 */ 516 public String getLabel() { 517 return label; 518 } 519 520 /** 521 * Sets the popup menu's label. Different look and feels may choose 522 * to display or not display this. 523 * 524 * @param label a string specifying the label for the popup menu 525 * 526 * @see #setLabel 527 */ 528 @BeanProperty(description 529 = "The label for the popup menu.") 530 public void setLabel(String label) { 531 String oldValue = this.label; 532 this.label = label; 533 firePropertyChange("label", oldValue, label); 534 if (accessibleContext != null) { 535 accessibleContext.firePropertyChange( 536 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 537 oldValue, label); 538 } 539 invalidate(); 540 repaint(); 541 } 542 543 /** 544 * Appends a new separator at the end of the menu. 545 */ 546 public void addSeparator() { 547 add( new JPopupMenu.Separator() ); 548 } 549 550 /** 551 * Inserts a menu item for the specified <code>Action</code> object at 552 * a given position. 553 * 554 * @param a the <code>Action</code> object to insert 555 * @param index specifies the position at which to insert the 556 * <code>Action</code>, where 0 is the first 557 * @exception IllegalArgumentException if <code>index</code> < 0 558 * @see Action 559 */ 560 public void insert(Action a, int index) { 561 JMenuItem mi = createActionComponent(a); 562 mi.setAction(a); 563 insert(mi, index); 564 } 565 566 /** 567 * Inserts the specified component into the menu at a given 568 * position. 569 * 570 * @param component the <code>Component</code> to insert 571 * @param index specifies the position at which 572 * to insert the component, where 0 is the first 573 * @exception IllegalArgumentException if <code>index</code> < 0 574 */ 575 public void insert(Component component, int index) { 576 if (index < 0) { 577 throw new IllegalArgumentException("index less than zero."); 578 } 579 580 int nitems = getComponentCount(); 581 // PENDING(ges): Why not use an array? 582 Vector<Component> tempItems = new Vector<Component>(); 583 584 /* Remove the item at index, nitems-index times 585 storing them in a temporary vector in the 586 order they appear on the menu. 587 */ 588 for (int i = index ; i < nitems; i++) { 589 tempItems.addElement(getComponent(index)); 590 remove(index); 591 } 592 593 add(component); 594 595 /* Add the removed items back to the menu, they are 596 already in the correct order in the temp vector. 597 */ 598 for (Component tempItem : tempItems) { 599 add(tempItem); 600 } 601 } 602 603 /** 604 * Adds a <code>PopupMenu</code> listener. 605 * 606 * @param l the <code>PopupMenuListener</code> to add 607 */ 608 public void addPopupMenuListener(PopupMenuListener l) { 609 listenerList.add(PopupMenuListener.class,l); 610 } 611 612 /** 613 * Removes a <code>PopupMenu</code> listener. 614 * 615 * @param l the <code>PopupMenuListener</code> to remove 616 */ 617 public void removePopupMenuListener(PopupMenuListener l) { 618 listenerList.remove(PopupMenuListener.class,l); 619 } 620 621 /** 622 * Returns an array of all the <code>PopupMenuListener</code>s added 623 * to this JMenuItem with addPopupMenuListener(). 624 * 625 * @return all of the <code>PopupMenuListener</code>s added or an empty 626 * array if no listeners have been added 627 * @since 1.4 628 */ 629 @BeanProperty(bound = false) 630 public PopupMenuListener[] getPopupMenuListeners() { 631 return listenerList.getListeners(PopupMenuListener.class); 632 } 633 634 /** 635 * Adds a <code>MenuKeyListener</code> to the popup menu. 636 * 637 * @param l the <code>MenuKeyListener</code> to be added 638 * @since 1.5 639 */ 640 public void addMenuKeyListener(MenuKeyListener l) { 641 listenerList.add(MenuKeyListener.class, l); 642 } 643 644 /** 645 * Removes a <code>MenuKeyListener</code> from the popup menu. 646 * 647 * @param l the <code>MenuKeyListener</code> to be removed 648 * @since 1.5 649 */ 650 public void removeMenuKeyListener(MenuKeyListener l) { 651 listenerList.remove(MenuKeyListener.class, l); 652 } 653 654 /** 655 * Returns an array of all the <code>MenuKeyListener</code>s added 656 * to this JPopupMenu with addMenuKeyListener(). 657 * 658 * @return all of the <code>MenuKeyListener</code>s added or an empty 659 * array if no listeners have been added 660 * @since 1.5 661 */ 662 @BeanProperty(bound = false) 663 public MenuKeyListener[] getMenuKeyListeners() { 664 return listenerList.getListeners(MenuKeyListener.class); 665 } 666 667 /** 668 * Notifies <code>PopupMenuListener</code>s that this popup menu will 669 * become visible. 670 */ 671 protected void firePopupMenuWillBecomeVisible() { 672 Object[] listeners = listenerList.getListenerList(); 673 PopupMenuEvent e=null; 674 for (int i = listeners.length-2; i>=0; i-=2) { 675 if (listeners[i]==PopupMenuListener.class) { 676 if (e == null) 677 e = new PopupMenuEvent(this); 678 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e); 679 } 680 } 681 } 682 683 /** 684 * Notifies <code>PopupMenuListener</code>s that this popup menu will 685 * become invisible. 686 */ 687 protected void firePopupMenuWillBecomeInvisible() { 688 Object[] listeners = listenerList.getListenerList(); 689 PopupMenuEvent e=null; 690 for (int i = listeners.length-2; i>=0; i-=2) { 691 if (listeners[i]==PopupMenuListener.class) { 692 if (e == null) 693 e = new PopupMenuEvent(this); 694 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e); 695 } 696 } 697 } 698 699 /** 700 * Notifies <code>PopupMenuListeners</code> that this popup menu is 701 * cancelled. 702 */ 703 protected void firePopupMenuCanceled() { 704 Object[] listeners = listenerList.getListenerList(); 705 PopupMenuEvent e=null; 706 for (int i = listeners.length-2; i>=0; i-=2) { 707 if (listeners[i]==PopupMenuListener.class) { 708 if (e == null) 709 e = new PopupMenuEvent(this); 710 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e); 711 } 712 } 713 } 714 715 /** 716 * Always returns true since popups, by definition, should always 717 * be on top of all other windows. 718 * @return true 719 */ 720 // package private 721 boolean alwaysOnTop() { 722 return true; 723 } 724 725 /** 726 * Lays out the container so that it uses the minimum space 727 * needed to display its contents. 728 */ 729 public void pack() { 730 if(popup != null) { 731 Dimension pref = getPreferredSize(); 732 733 if (pref == null || pref.width != getWidth() || 734 pref.height != getHeight()) { 735 showPopup(); 736 } else { 737 validate(); 738 } 739 } 740 } 741 742 /** 743 * Sets the visibility of the popup menu. 744 * 745 * @param b true to make the popup visible, or false to 746 * hide it 747 */ 748 @BeanProperty(description 749 = "Makes the popup visible") 750 public void setVisible(boolean b) { 751 if (DEBUG) { 752 System.out.println("JPopupMenu.setVisible " + b); 753 } 754 755 // Is it a no-op? 756 if (b == isVisible()) 757 return; 758 759 // if closing, first close all Submenus 760 if (b == false) { 761 762 // 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is 763 // a protected method and cannot be called from BasicPopupMenuUI directly 764 // The real solution could be to make 765 // firePopupMenuCanceled public and call it directly. 766 Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled"); 767 if (doCanceled != null && doCanceled == Boolean.TRUE) { 768 putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE); 769 firePopupMenuCanceled(); 770 } 771 getSelectionModel().clearSelection(); 772 773 } else { 774 // This is a popup menu with MenuElement children, 775 // set selection path before popping up! 776 if (isPopupMenu()) { 777 MenuElement me[] = new MenuElement[1]; 778 me[0] = this; 779 MenuSelectionManager.defaultManager().setSelectedPath(me); 780 } 781 } 782 783 if(b) { 784 firePopupMenuWillBecomeVisible(); 785 showPopup(); 786 firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE); 787 788 789 } else if(popup != null) { 790 firePopupMenuWillBecomeInvisible(); 791 popup.hide(); 792 popup = null; 793 firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE); 794 // 4694797: When popup menu is made invisible, selected path 795 // should be cleared 796 if (isPopupMenu()) { 797 MenuSelectionManager.defaultManager().clearSelectedPath(); 798 } 799 } 800 } 801 802 /** 803 * Retrieves <code>Popup</code> instance from the 804 * <code>PopupMenuUI</code> that has had <code>show</code> invoked on 805 * it. If the current <code>popup</code> is non-null, 806 * this will invoke <code>dispose</code> of it, and then 807 * <code>show</code> the new one. 808 * <p> 809 * This does NOT fire any events, it is up the caller to dispatch 810 * the necessary events. 811 */ 812 private void showPopup() { 813 Popup oldPopup = popup; 814 815 if (oldPopup != null) { 816 oldPopup.hide(); 817 } 818 PopupFactory popupFactory = PopupFactory.getSharedInstance(); 819 820 if (isLightWeightPopupEnabled()) { 821 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP); 822 } 823 else { 824 popupFactory.setPopupType(PopupFactory.HEAVY_WEIGHT_POPUP); 825 } 826 827 // adjust the location of the popup 828 Point p = adjustPopupLocationToFitScreen(desiredLocationX,desiredLocationY); 829 desiredLocationX = p.x; 830 desiredLocationY = p.y; 831 832 Popup newPopup = getUI().getPopup(this, desiredLocationX, 833 desiredLocationY); 834 835 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP); 836 popup = newPopup; 837 newPopup.show(); 838 } 839 840 /** 841 * Returns true if the popup menu is visible (currently 842 * being displayed). 843 */ 844 public boolean isVisible() { 845 return popup != null; 846 } 847 848 /** 849 * Sets the location of the upper left corner of the 850 * popup menu using x, y coordinates. 851 * <p> 852 * The method changes the geometry-related data. Therefore, 853 * the native windowing system may ignore such requests, or it may modify 854 * the requested data, so that the {@code JPopupMenu} object is placed and sized 855 * in a way that corresponds closely to the desktop settings. 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 */ 862 @BeanProperty(description 863 = "The location of the popup menu.") 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 showPopup(); 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 */ 902 @BeanProperty(bound = false, expert = true, description 903 = "The invoking component for the popup menu") 904 public void setInvoker(Component invoker) { 905 Component oldInvoker = this.invoker; 906 this.invoker = invoker; 907 if ((oldInvoker != this.invoker) && (ui != null)) { 908 ui.uninstallUI(this); 909 ui.installUI(this); 910 } 911 invalidate(); 912 } 913 914 /** 915 * Displays the popup menu at the position x,y in the coordinate 916 * space of the component invoker. 917 * 918 * @param invoker the component in whose space the popup menu is to appear 919 * @param x the x coordinate in invoker's coordinate space at which 920 * the popup menu is to be displayed 921 * @param y the y coordinate in invoker's coordinate space at which 922 * the popup menu is to be displayed 923 */ 924 public void show(Component invoker, int x, int y) { 925 if (DEBUG) { 926 System.out.println("in JPopupMenu.show " ); 927 } 928 setInvoker(invoker); 929 Frame newFrame = getFrame(invoker); 930 if (newFrame != frame) { 931 // Use the invoker's frame so that events 932 // are propagated properly 933 if (newFrame!=null) { 934 this.frame = newFrame; 935 if(popup != null) { 936 setVisible(false); 937 } 938 } 939 } 940 Point invokerOrigin; 941 if (invoker != null) { 942 invokerOrigin = invoker.getLocationOnScreen(); 943 944 // To avoid integer overflow 945 long lx, ly; 946 lx = ((long) invokerOrigin.x) + 947 ((long) x); 948 ly = ((long) invokerOrigin.y) + 949 ((long) y); 950 if(lx > Integer.MAX_VALUE) lx = Integer.MAX_VALUE; 951 if(lx < Integer.MIN_VALUE) lx = Integer.MIN_VALUE; 952 if(ly > Integer.MAX_VALUE) ly = Integer.MAX_VALUE; 953 if(ly < Integer.MIN_VALUE) ly = Integer.MIN_VALUE; 954 955 setLocation((int) lx, (int) ly); 956 } else { 957 setLocation(x, y); 958 } 959 setVisible(true); 960 } 961 962 /** 963 * Returns the popup menu which is at the root of the menu system 964 * for this popup menu. 965 * 966 * @return the topmost grandparent <code>JPopupMenu</code> 967 */ 968 JPopupMenu getRootPopupMenu() { 969 JPopupMenu mp = this; 970 while((mp!=null) && (mp.isPopupMenu()!=true) && 971 (mp.getInvoker() != null) && 972 (mp.getInvoker().getParent() != null) && 973 (mp.getInvoker().getParent() instanceof JPopupMenu) 974 ) { 975 mp = (JPopupMenu) mp.getInvoker().getParent(); 976 } 977 return mp; 978 } 979 980 /** 981 * Returns the component at the specified index. 982 * 983 * @param i the index of the component, where 0 is the first 984 * @return the <code>Component</code> at that index 985 * @deprecated replaced by {@link java.awt.Container#getComponent(int)} 986 */ 987 @Deprecated 988 public Component getComponentAtIndex(int i) { 989 return getComponent(i); 990 } 991 992 /** 993 * Returns the index of the specified component. 994 * 995 * @param c the <code>Component</code> to find 996 * @return the index of the component, where 0 is the first; 997 * or -1 if the component is not found 998 */ 999 public int getComponentIndex(Component c) { 1000 int ncomponents = this.getComponentCount(); 1001 Component[] component = this.getComponents(); 1002 for (int i = 0 ; i < ncomponents ; i++) { 1003 Component comp = component[i]; 1004 if (comp == c) 1005 return i; 1006 } 1007 return -1; 1008 } 1009 1010 /** 1011 * Sets the size of the Popup window using a <code>Dimension</code> object. 1012 * This is equivalent to <code>setPreferredSize(d)</code>. 1013 * 1014 * @param d the <code>Dimension</code> specifying the new size 1015 * of this component. 1016 */ 1017 @BeanProperty(description 1018 = "The size of the popup menu") 1019 public void setPopupSize(Dimension d) { 1020 Dimension oldSize = getPreferredSize(); 1021 1022 setPreferredSize(d); 1023 if (popup != null) { 1024 Dimension newSize = getPreferredSize(); 1025 1026 if (!oldSize.equals(newSize)) { 1027 showPopup(); 1028 } 1029 } 1030 } 1031 1032 /** 1033 * Sets the size of the Popup window to the specified width and 1034 * height. This is equivalent to 1035 * <code>setPreferredSize(new Dimension(width, height))</code>. 1036 * 1037 * @param width the new width of the Popup in pixels 1038 * @param height the new height of the Popup in pixels 1039 */ 1040 @BeanProperty(description 1041 = "The size of the popup menu") 1042 public void setPopupSize(int width, int height) { 1043 setPopupSize(new Dimension(width, height)); 1044 } 1045 1046 /** 1047 * Sets the currently selected component, This will result 1048 * in a change to the selection model. 1049 * 1050 * @param sel the <code>Component</code> to select 1051 */ 1052 @BeanProperty(expert = true, hidden = true, description 1053 = "The selected component on the popup menu") 1054 public void setSelected(Component sel) { 1055 SingleSelectionModel model = getSelectionModel(); 1056 int index = getComponentIndex(sel); 1057 model.setSelectedIndex(index); 1058 } 1059 1060 /** 1061 * Checks whether the border should be painted. 1062 * 1063 * @return true if the border is painted, false otherwise 1064 * @see #setBorderPainted 1065 */ 1066 public boolean isBorderPainted() { 1067 return paintBorder; 1068 } 1069 1070 /** 1071 * Sets whether the border should be painted. 1072 * 1073 * @param b if true, the border is painted. 1074 * @see #isBorderPainted 1075 */ 1076 @BeanProperty(bound = false, description 1077 = "Is the border of the popup menu painted") 1078 public void setBorderPainted(boolean b) { 1079 paintBorder = b; 1080 repaint(); 1081 } 1082 1083 /** 1084 * Paints the popup menu's border if the <code>borderPainted</code> 1085 * property is <code>true</code>. 1086 * @param g the <code>Graphics</code> object 1087 * 1088 * @see JComponent#paint 1089 * @see JComponent#setBorder 1090 */ 1091 protected void paintBorder(Graphics g) { 1092 if (isBorderPainted()) { 1093 super.paintBorder(g); 1094 } 1095 } 1096 1097 /** 1098 * Returns the margin, in pixels, between the popup menu's border and 1099 * its containers. 1100 * 1101 * @return an <code>Insets</code> object containing the margin values. 1102 */ 1103 @BeanProperty(bound = false) 1104 public Insets getMargin() { 1105 if(margin == null) { 1106 return new Insets(0,0,0,0); 1107 } else { 1108 return margin; 1109 } 1110 } 1111 1112 1113 /** 1114 * Examines the list of menu items to determine whether 1115 * <code>popup</code> is a popup menu. 1116 * 1117 * @param popup a <code>JPopupMenu</code> 1118 * @return true if <code>popup</code> 1119 */ 1120 boolean isSubPopupMenu(JPopupMenu popup) { 1121 int ncomponents = this.getComponentCount(); 1122 Component[] component = this.getComponents(); 1123 for (int i = 0 ; i < ncomponents ; i++) { 1124 Component comp = component[i]; 1125 if (comp instanceof JMenu) { 1126 JMenu menu = (JMenu)comp; 1127 JPopupMenu subPopup = menu.getPopupMenu(); 1128 if (subPopup == popup) 1129 return true; 1130 if (subPopup.isSubPopupMenu(popup)) 1131 return true; 1132 } 1133 } 1134 return false; 1135 } 1136 1137 1138 private static Frame getFrame(Component c) { 1139 Component w = c; 1140 1141 while(!(w instanceof Frame) && (w!=null)) { 1142 w = w.getParent(); 1143 } 1144 return (Frame)w; 1145 } 1146 1147 1148 /** 1149 * Returns a string representation of this <code>JPopupMenu</code>. 1150 * This method 1151 * is intended to be used only for debugging purposes, and the 1152 * content and format of the returned string may vary between 1153 * implementations. The returned string may be empty but may not 1154 * be <code>null</code>. 1155 * 1156 * @return a string representation of this <code>JPopupMenu</code>. 1157 */ 1158 protected String paramString() { 1159 String labelString = (label != null ? 1160 label : ""); 1161 String paintBorderString = (paintBorder ? 1162 "true" : "false"); 1163 String marginString = (margin != null ? 1164 margin.toString() : ""); 1165 String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ? 1166 "true" : "false"); 1167 return super.paramString() + 1168 ",desiredLocationX=" + desiredLocationX + 1169 ",desiredLocationY=" + desiredLocationY + 1170 ",label=" + labelString + 1171 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString + 1172 ",margin=" + marginString + 1173 ",paintBorder=" + paintBorderString; 1174 } 1175 1176 ///////////////// 1177 // Accessibility support 1178 //////////////// 1179 1180 /** 1181 * Gets the AccessibleContext associated with this JPopupMenu. 1182 * For JPopupMenus, the AccessibleContext takes the form of an 1183 * AccessibleJPopupMenu. 1184 * A new AccessibleJPopupMenu instance is created if necessary. 1185 * 1186 * @return an AccessibleJPopupMenu that serves as the 1187 * AccessibleContext of this JPopupMenu 1188 */ 1189 @BeanProperty(bound = false) 1190 public AccessibleContext getAccessibleContext() { 1191 if (accessibleContext == null) { 1192 accessibleContext = new AccessibleJPopupMenu(); 1193 } 1194 return accessibleContext; 1195 } 1196 1197 /** 1198 * This class implements accessibility support for the 1199 * <code>JPopupMenu</code> class. It provides an implementation of the 1200 * Java Accessibility API appropriate to popup menu user-interface 1201 * elements. 1202 */ 1203 @SuppressWarnings("serial") 1204 protected class AccessibleJPopupMenu extends AccessibleJComponent 1205 implements PropertyChangeListener { 1206 1207 /** 1208 * AccessibleJPopupMenu constructor 1209 * 1210 * @since 1.5 1211 */ 1212 protected AccessibleJPopupMenu() { 1213 JPopupMenu.this.addPropertyChangeListener(this); 1214 } 1215 1216 /** 1217 * Get the role of this object. 1218 * 1219 * @return an instance of AccessibleRole describing the role of 1220 * the object 1221 */ 1222 public AccessibleRole getAccessibleRole() { 1223 return AccessibleRole.POPUP_MENU; 1224 } 1225 1226 /** 1227 * This method gets called when a bound property is changed. 1228 * @param e A <code>PropertyChangeEvent</code> object describing 1229 * the event source and the property that has changed. Must not be null. 1230 * 1231 * @throws NullPointerException if the parameter is null. 1232 * @since 1.5 1233 */ 1234 public void propertyChange(PropertyChangeEvent e) { 1235 String propertyName = e.getPropertyName(); 1236 if (propertyName == "visible") { 1237 if (e.getOldValue() == Boolean.FALSE && 1238 e.getNewValue() == Boolean.TRUE) { 1239 handlePopupIsVisibleEvent(true); 1240 1241 } else if (e.getOldValue() == Boolean.TRUE && 1242 e.getNewValue() == Boolean.FALSE) { 1243 handlePopupIsVisibleEvent(false); 1244 } 1245 } 1246 } 1247 1248 /* 1249 * Handles popup "visible" PropertyChangeEvent 1250 */ 1251 private void handlePopupIsVisibleEvent(boolean visible) { 1252 if (visible) { 1253 // notify listeners that the popup became visible 1254 firePropertyChange(ACCESSIBLE_STATE_PROPERTY, 1255 null, AccessibleState.VISIBLE); 1256 // notify listeners that a popup list item is selected 1257 fireActiveDescendant(); 1258 } else { 1259 // notify listeners that the popup became hidden 1260 firePropertyChange(ACCESSIBLE_STATE_PROPERTY, 1261 AccessibleState.VISIBLE, null); 1262 } 1263 } 1264 1265 /* 1266 * Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners 1267 * on the popup menu invoker that a popup list item has been selected 1268 */ 1269 private void fireActiveDescendant() { 1270 if (JPopupMenu.this instanceof BasicComboPopup) { 1271 // get the popup list 1272 JList<?> popupList = ((BasicComboPopup)JPopupMenu.this).getList(); 1273 if (popupList == null) { 1274 return; 1275 } 1276 1277 // get the first selected item 1278 AccessibleContext ac = popupList.getAccessibleContext(); 1279 AccessibleSelection selection = ac.getAccessibleSelection(); 1280 if (selection == null) { 1281 return; 1282 } 1283 Accessible a = selection.getAccessibleSelection(0); 1284 if (a == null) { 1285 return; 1286 } 1287 AccessibleContext selectedItem = a.getAccessibleContext(); 1288 1289 // fire the event with the popup invoker as the source. 1290 if (selectedItem != null && invoker != null) { 1291 AccessibleContext invokerContext = invoker.getAccessibleContext(); 1292 if (invokerContext != null) { 1293 // Check invokerContext because Component.getAccessibleContext 1294 // returns null. Classes that extend Component are responsible 1295 // for returning a non-null AccessibleContext. 1296 invokerContext.firePropertyChange( 1297 ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, 1298 null, selectedItem); 1299 } 1300 } 1301 } 1302 } 1303 } // inner class AccessibleJPopupMenu 1304 1305 1306 //////////// 1307 // Serialization support. 1308 //////////// 1309 private void writeObject(ObjectOutputStream s) throws IOException { 1310 Vector<Object> values = new Vector<Object>(); 1311 1312 s.defaultWriteObject(); 1313 // Save the invoker, if its Serializable. 1314 if(invoker != null && invoker instanceof Serializable) { 1315 values.addElement("invoker"); 1316 values.addElement(invoker); 1317 } 1318 // Save the popup, if its Serializable. 1319 if(popup != null && popup instanceof Serializable) { 1320 values.addElement("popup"); 1321 values.addElement(popup); 1322 } 1323 s.writeObject(values); 1324 1325 if (getUIClassID().equals(uiClassID)) { 1326 byte count = JComponent.getWriteObjCounter(this); 1327 JComponent.setWriteObjCounter(this, --count); 1328 if (count == 0 && ui != null) { 1329 ui.installUI(this); 1330 } 1331 } 1332 } 1333 1334 // implements javax.swing.MenuElement 1335 private void readObject(ObjectInputStream s) 1336 throws IOException, ClassNotFoundException { 1337 ObjectInputStream.GetField f = s.readFields(); 1338 1339 int newDesiredLocationX = f.get("desiredLocationX", 0); 1340 int newDesiredLocationY = f.get("desiredLocationY", 0); 1341 Point p = adjustPopupLocationToFitScreen( 1342 newDesiredLocationX, newDesiredLocationY); 1343 desiredLocationX = p.x; 1344 desiredLocationY = p.y; 1345 1346 label = (String) f.get("label", null); 1347 paintBorder = f.get("paintBorder", false); 1348 margin = (Insets) f.get("margin", null); 1349 lightWeightPopup = f.get("lightWeightPopup", false); 1350 selectionModel = (SingleSelectionModel) f.get("selectionModel", null); 1351 1352 Vector<?> values = (Vector)s.readObject(); 1353 int indexCounter = 0; 1354 int maxCounter = values.size(); 1355 1356 if(indexCounter < maxCounter && values.elementAt(indexCounter). 1357 equals("invoker")) { 1358 invoker = (Component)values.elementAt(++indexCounter); 1359 indexCounter++; 1360 } 1361 if(indexCounter < maxCounter && values.elementAt(indexCounter). 1362 equals("popup")) { 1363 popup = (Popup)values.elementAt(++indexCounter); 1364 indexCounter++; 1365 } 1366 } 1367 1368 1369 /** 1370 * This method is required to conform to the 1371 * <code>MenuElement</code> interface, but it not implemented. 1372 * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager) 1373 */ 1374 public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {} 1375 1376 /** 1377 * Processes a key event forwarded from the 1378 * <code>MenuSelectionManager</code> and changes the menu selection, 1379 * if necessary, by using <code>MenuSelectionManager</code>'s API. 1380 * <p> 1381 * Note: you do not have to forward the event to sub-components. 1382 * This is done automatically by the <code>MenuSelectionManager</code>. 1383 * 1384 * @param e a <code>KeyEvent</code> 1385 * @param path the <code>MenuElement</code> path array 1386 * @param manager the <code>MenuSelectionManager</code> 1387 */ 1388 public void processKeyEvent(KeyEvent e, MenuElement path[], 1389 MenuSelectionManager manager) { 1390 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(), 1391 e.getWhen(), e.getModifiers(), 1392 e.getKeyCode(), e.getKeyChar(), 1393 path, manager); 1394 processMenuKeyEvent(mke); 1395 1396 if (mke.isConsumed()) { 1397 e.consume(); 1398 } 1399 } 1400 1401 /** 1402 * Handles a keystroke in a menu. 1403 * 1404 * @param e a <code>MenuKeyEvent</code> object 1405 * @since 1.5 1406 */ 1407 private void processMenuKeyEvent(MenuKeyEvent e) { 1408 switch (e.getID()) { 1409 case KeyEvent.KEY_PRESSED: 1410 fireMenuKeyPressed(e); break; 1411 case KeyEvent.KEY_RELEASED: 1412 fireMenuKeyReleased(e); break; 1413 case KeyEvent.KEY_TYPED: 1414 fireMenuKeyTyped(e); break; 1415 default: 1416 break; 1417 } 1418 } 1419 1420 /** 1421 * Notifies all listeners that have registered interest for 1422 * notification on this event type. 1423 * 1424 * @param event a <code>MenuKeyEvent</code> 1425 * @see EventListenerList 1426 */ 1427 private void fireMenuKeyPressed(MenuKeyEvent event) { 1428 Object[] listeners = listenerList.getListenerList(); 1429 for (int i = listeners.length-2; i>=0; i-=2) { 1430 if (listeners[i]==MenuKeyListener.class) { 1431 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event); 1432 } 1433 } 1434 } 1435 1436 /** 1437 * Notifies all listeners that have registered interest for 1438 * notification on this event type. 1439 * 1440 * @param event a <code>MenuKeyEvent</code> 1441 * @see EventListenerList 1442 */ 1443 private void fireMenuKeyReleased(MenuKeyEvent event) { 1444 Object[] listeners = listenerList.getListenerList(); 1445 for (int i = listeners.length-2; i>=0; i-=2) { 1446 if (listeners[i]==MenuKeyListener.class) { 1447 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event); 1448 } 1449 } 1450 } 1451 1452 /** 1453 * Notifies all listeners that have registered interest for 1454 * notification on this event type. 1455 * 1456 * @param event a <code>MenuKeyEvent</code> 1457 * @see EventListenerList 1458 */ 1459 private void fireMenuKeyTyped(MenuKeyEvent event) { 1460 Object[] listeners = listenerList.getListenerList(); 1461 for (int i = listeners.length-2; i>=0; i-=2) { 1462 if (listeners[i]==MenuKeyListener.class) { 1463 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event); 1464 } 1465 } 1466 } 1467 1468 /** 1469 * Messaged when the menubar selection changes to activate or 1470 * deactivate this menu. This implements the 1471 * <code>javax.swing.MenuElement</code> interface. 1472 * Overrides <code>MenuElement.menuSelectionChanged</code>. 1473 * 1474 * @param isIncluded true if this menu is active, false if 1475 * it is not 1476 * @see MenuElement#menuSelectionChanged(boolean) 1477 */ 1478 public void menuSelectionChanged(boolean isIncluded) { 1479 if (DEBUG) { 1480 System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded); 1481 } 1482 if(invoker instanceof JMenu) { 1483 JMenu m = (JMenu) invoker; 1484 if(isIncluded) 1485 m.setPopupMenuVisible(true); 1486 else 1487 m.setPopupMenuVisible(false); 1488 } 1489 if (isPopupMenu() && !isIncluded) 1490 setVisible(false); 1491 } 1492 1493 /** 1494 * Returns an array of <code>MenuElement</code>s containing the submenu 1495 * for this menu component. It will only return items conforming to 1496 * the <code>JMenuElement</code> interface. 1497 * If popup menu is <code>null</code> returns 1498 * an empty array. This method is required to conform to the 1499 * <code>MenuElement</code> interface. 1500 * 1501 * @return an array of <code>MenuElement</code> objects 1502 * @see MenuElement#getSubElements 1503 */ 1504 @BeanProperty(bound = false) 1505 public MenuElement[] getSubElements() { 1506 MenuElement result[]; 1507 Vector<MenuElement> tmp = new Vector<MenuElement>(); 1508 int c = getComponentCount(); 1509 int i; 1510 Component m; 1511 1512 for(i=0 ; i < c ; i++) { 1513 m = getComponent(i); 1514 if(m instanceof MenuElement) 1515 tmp.addElement((MenuElement) m); 1516 } 1517 1518 result = new MenuElement[tmp.size()]; 1519 for(i=0,c=tmp.size() ; i < c ; i++) 1520 result[i] = tmp.elementAt(i); 1521 return result; 1522 } 1523 1524 /** 1525 * Returns this <code>JPopupMenu</code> component. 1526 * @return this <code>JPopupMenu</code> object 1527 * @see MenuElement#getComponent 1528 */ 1529 public Component getComponent() { 1530 return this; 1531 } 1532 1533 1534 /** 1535 * A popup menu-specific separator. 1536 */ 1537 @SuppressWarnings("serial") 1538 static public class Separator extends JSeparator 1539 { 1540 /** 1541 * Constructs a popup menu-specific Separator. 1542 */ 1543 public Separator( ) 1544 { 1545 super( JSeparator.HORIZONTAL ); 1546 } 1547 1548 /** 1549 * Returns the name of the L&F class that renders this component. 1550 * 1551 * @return the string "PopupMenuSeparatorUI" 1552 * @see JComponent#getUIClassID 1553 * @see UIDefaults#getUI 1554 */ 1555 public String getUIClassID() 1556 { 1557 return "PopupMenuSeparatorUI"; 1558 1559 } 1560 } 1561 1562 /** 1563 * Returns true if the <code>MouseEvent</code> is considered a popup trigger 1564 * by the <code>JPopupMenu</code>'s currently installed UI. 1565 * 1566 * @param e a {@code MouseEvent} 1567 * @return true if the mouse event is a popup trigger 1568 * @since 1.3 1569 */ 1570 public boolean isPopupTrigger(MouseEvent e) { 1571 return getUI().isPopupTrigger(e); 1572 } 1573 }