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