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