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