jdk/src/share/classes/javax/swing/JPopupMenu.java
changeset 2 90ce3da70b43
child 675 4f26ea16c5c1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/JPopupMenu.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1521 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.swing;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.beans.*;
+
+import java.util.Locale;
+import java.util.Vector;
+import java.util.Hashtable;
+import javax.accessibility.*;
+import javax.swing.plaf.PopupMenuUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicComboPopup;
+import javax.swing.event.*;
+
+import java.applet.Applet;
+
+/**
+ * An implementation of a popup menu -- a small window that pops up
+ * and displays a series of choices. A <code>JPopupMenu</code> is used for the
+ * menu that appears when the user selects an item on the menu bar.
+ * It is also used for "pull-right" menu that appears when the
+ * selects a menu item that activates it. Finally, a <code>JPopupMenu</code>
+ * can also be used anywhere else you want a menu to appear.  For
+ * example, when the user right-clicks in a specified area.
+ * <p>
+ * For information and examples of using popup menus, see
+ * <a
+ href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
+ * in <em>The Java Tutorial.</em>
+ * <p>
+ * <strong>Warning:</strong> Swing is not thread safe. For more
+ * information see <a
+ * href="package-summary.html#threading">Swing's Threading
+ * Policy</a>.
+ * <p>
+ * <strong>Warning:</strong>
+ * Serialized objects of this class will not be compatible with
+ * future Swing releases. The current serialization support is
+ * appropriate for short term storage or RMI between applications running
+ * the same version of Swing.  As of 1.4, support for long term storage
+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
+ * has been added to the <code>java.beans</code> package.
+ * Please see {@link java.beans.XMLEncoder}.
+ *
+ * @beaninfo
+ *   attribute: isContainer false
+ * description: A small window that pops up and displays a series of choices.
+ *
+ * @author Georges Saab
+ * @author David Karlton
+ * @author Arnaud Weber
+ */
+public class JPopupMenu extends JComponent implements Accessible,MenuElement {
+
+    /**
+     * @see #getUIClassID
+     * @see #readObject
+     */
+    private static final String uiClassID = "PopupMenuUI";
+
+    /**
+     * Key used in AppContext to determine if light way popups are the default.
+     */
+    private static final Object defaultLWPopupEnabledKey =
+        new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
+
+    /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
+    static boolean popupPostionFixDisabled = false;
+
+    static {
+        popupPostionFixDisabled = java.security.AccessController.doPrivileged(
+                new sun.security.action.GetPropertyAction(
+                "javax.swing.adjustPopupLocationToFit","")).equals("false");
+
+    }
+
+    transient  Component invoker;
+    transient  Popup popup;
+    transient  Frame frame;
+    private    int desiredLocationX,desiredLocationY;
+
+    private    String     label                   = null;
+    private    boolean   paintBorder              = true;
+    private    Insets    margin                   = null;
+
+    /**
+     * Used to indicate if lightweight popups should be used.
+     */
+    private    boolean   lightWeightPopup         = true;
+
+    /*
+     * Model for the selected subcontrol.
+     */
+    private SingleSelectionModel selectionModel;
+
+    /* Lock object used in place of class object for synchronization.
+     * (4187686)
+     */
+    private static final Object classLock = new Object();
+
+    /* diagnostic aids -- should be false for production builds. */
+    private static final boolean TRACE =   false; // trace creates and disposes
+    private static final boolean VERBOSE = false; // show reuse hits/misses
+    private static final boolean DEBUG =   false;  // show bad params, misc.
+
+    /**
+     *  Sets the default value of the <code>lightWeightPopupEnabled</code>
+     *  property.
+     *
+     *  @param aFlag <code>true</code> if popups can be lightweight,
+     *               otherwise <code>false</code>
+     *  @see #getDefaultLightWeightPopupEnabled
+     *  @see #setLightWeightPopupEnabled
+     */
+    public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
+        SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
+                                     Boolean.valueOf(aFlag));
+    }
+
+    /**
+     *  Gets the <code>defaultLightWeightPopupEnabled</code> property,
+     *  which by default is <code>true</code>.
+     *
+     *  @return the value of the <code>defaultLightWeightPopupEnabled</code>
+     *          property
+     *
+     *  @see #setDefaultLightWeightPopupEnabled
+     */
+    public static boolean getDefaultLightWeightPopupEnabled() {
+        Boolean b = (Boolean)
+            SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
+        if (b == null) {
+            SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
+                                         Boolean.TRUE);
+            return true;
+        }
+        return b.booleanValue();
+    }
+
+    /**
+     * Constructs a <code>JPopupMenu</code> without an "invoker".
+     */
+    public JPopupMenu() {
+        this(null);
+    }
+
+    /**
+     * Constructs a <code>JPopupMenu</code> with the specified title.
+     *
+     * @param label  the string that a UI may use to display as a title
+     * for the popup menu.
+     */
+    public JPopupMenu(String label) {
+        this.label = label;
+        lightWeightPopup = getDefaultLightWeightPopupEnabled();
+        setSelectionModel(new DefaultSingleSelectionModel());
+        enableEvents(AWTEvent.MOUSE_EVENT_MASK);
+        setFocusTraversalKeysEnabled(false);
+        updateUI();
+    }
+
+
+
+    /**
+     * Returns the look and feel (L&F) object that renders this component.
+     *
+     * @return the <code>PopupMenuUI</code> object that renders this component
+     */
+    public PopupMenuUI getUI() {
+        return (PopupMenuUI)ui;
+    }
+
+    /**
+     * Sets the L&F object that renders this component.
+     *
+     * @param ui the new <code>PopupMenuUI</code> L&F object
+     * @see UIDefaults#getUI
+     * @beaninfo
+     *        bound: true
+     *       hidden: true
+     *    attribute: visualUpdate true
+     *  description: The UI object that implements the Component's LookAndFeel.
+     */
+    public void setUI(PopupMenuUI ui) {
+        super.setUI(ui);
+    }
+
+    /**
+     * Resets the UI property to a value from the current look and feel.
+     *
+     * @see JComponent#updateUI
+     */
+    public void updateUI() {
+        setUI((PopupMenuUI)UIManager.getUI(this));
+    }
+
+
+    /**
+     * Returns the name of the L&F class that renders this component.
+     *
+     * @return the string "PopupMenuUI"
+     * @see JComponent#getUIClassID
+     * @see UIDefaults#getUI
+     */
+    public String getUIClassID() {
+        return uiClassID;
+    }
+
+    protected void processFocusEvent(FocusEvent evt) {
+        super.processFocusEvent(evt);
+    }
+
+    /**
+     * Processes key stroke events such as mnemonics and accelerators.
+     *
+     * @param evt  the key event to be processed
+     */
+    protected void processKeyEvent(KeyEvent evt) {
+        MenuSelectionManager.defaultManager().processKeyEvent(evt);
+        if (evt.isConsumed()) {
+            return;
+        }
+        super.processKeyEvent(evt);
+    }
+
+
+    /**
+     * Returns the model object that handles single selections.
+     *
+     * @return the <code>selectionModel</code> property
+     * @see SingleSelectionModel
+     */
+    public SingleSelectionModel getSelectionModel() {
+        return selectionModel;
+    }
+
+    /**
+     * Sets the model object to handle single selections.
+     *
+     * @param model the new <code>SingleSelectionModel</code>
+     * @see SingleSelectionModel
+     * @beaninfo
+     * description: The selection model for the popup menu
+     *      expert: true
+     */
+    public void setSelectionModel(SingleSelectionModel model) {
+        selectionModel = model;
+    }
+
+    /**
+     * Appends the specified menu item to the end of this menu.
+     *
+     * @param menuItem the <code>JMenuItem</code> to add
+     * @return the <code>JMenuItem</code> added
+     */
+    public JMenuItem add(JMenuItem menuItem) {
+        super.add(menuItem);
+        return menuItem;
+    }
+
+    /**
+     * Creates a new menu item with the specified text and appends
+     * it to the end of this menu.
+     *
+     * @param s the string for the menu item to be added
+     */
+    public JMenuItem add(String s) {
+        return add(new JMenuItem(s));
+    }
+
+    /**
+     * Appends a new menu item to the end of the menu which
+     * dispatches the specified <code>Action</code> object.
+     *
+     * @param a the <code>Action</code> to add to the menu
+     * @return the new menu item
+     * @see Action
+     */
+    public JMenuItem add(Action a) {
+        JMenuItem mi = createActionComponent(a);
+        mi.setAction(a);
+        add(mi);
+        return mi;
+    }
+
+    /**
+     * Returns an point which has been adjusted to take into account of the
+     * desktop bounds, taskbar and multi-monitor configuration.
+     * <p>
+     * This adustment may be cancelled by invoking the application with
+     * -Djavax.swing.adjustPopupLocationToFit=false
+     */
+    Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
+        Point p = new Point(xposition, yposition);
+
+        if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
+            return p;
+
+        Toolkit toolkit = Toolkit.getDefaultToolkit();
+        Rectangle screenBounds;
+        GraphicsConfiguration gc = null;
+        // Try to find GraphicsConfiguration, that includes mouse
+        // pointer position
+        GraphicsEnvironment ge =
+            GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsDevice[] gd = ge.getScreenDevices();
+        for(int i = 0; i < gd.length; i++) {
+            if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
+                GraphicsConfiguration dgc =
+                    gd[i].getDefaultConfiguration();
+                if(dgc.getBounds().contains(p)) {
+                    gc = dgc;
+                    break;
+                }
+            }
+        }
+
+        // If not found and we have invoker, ask invoker about his gc
+        if(gc == null && getInvoker() != null) {
+            gc = getInvoker().getGraphicsConfiguration();
+        }
+
+        if(gc != null) {
+            // If we have GraphicsConfiguration use it to get
+            // screen bounds
+            screenBounds = gc.getBounds();
+        } else {
+            // If we don't have GraphicsConfiguration use primary screen
+            screenBounds = new Rectangle(toolkit.getScreenSize());
+        }
+
+        Dimension size;
+
+        size = JPopupMenu.this.getPreferredSize();
+
+        // Use long variables to prevent overflow
+        long pw = (long) p.x + (long) size.width;
+        long ph = (long) p.y + (long) size.height;
+
+        if( pw > screenBounds.x + screenBounds.width )
+             p.x = screenBounds.x + screenBounds.width - size.width;
+
+        if( ph > screenBounds.y + screenBounds.height)
+             p.y = screenBounds.y + screenBounds.height - size.height;
+
+        /* Change is made to the desired (X,Y) values, when the
+           PopupMenu is too tall OR too wide for the screen
+        */
+        if( p.x < screenBounds.x )
+            p.x = screenBounds.x ;
+        if( p.y < screenBounds.y )
+            p.y = screenBounds.y;
+
+        return p;
+    }
+
+
+    /**
+     * Factory method which creates the <code>JMenuItem</code> for
+     * <code>Actions</code> added to the <code>JPopupMenu</code>.
+     *
+     * @param a the <code>Action</code> for the menu item to be added
+     * @return the new menu item
+     * @see Action
+     *
+     * @since 1.3
+     */
+    protected JMenuItem createActionComponent(Action a) {
+        JMenuItem mi = new JMenuItem() {
+            protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
+                PropertyChangeListener pcl = createActionChangeListener(this);
+                if (pcl == null) {
+                    pcl = super.createActionPropertyChangeListener(a);
+                }
+                return pcl;
+            }
+        };
+        mi.setHorizontalTextPosition(JButton.TRAILING);
+        mi.setVerticalTextPosition(JButton.CENTER);
+        return mi;
+    }
+
+    /**
+     * Returns a properly configured <code>PropertyChangeListener</code>
+     * which updates the control as changes to the <code>Action</code> occur.
+     */
+    protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
+        return b.createActionPropertyChangeListener0(b.getAction());
+    }
+
+    /**
+     * Removes the component at the specified index from this popup menu.
+     *
+     * @param       pos the position of the item to be removed
+     * @exception   IllegalArgumentException if the value of
+     *                          <code>pos</code> < 0, or if the value of
+     *                          <code>pos</code> is greater than the
+     *                          number of items
+     */
+    public void remove(int pos) {
+        if (pos < 0) {
+            throw new IllegalArgumentException("index less than zero.");
+        }
+        if (pos > getComponentCount() -1) {
+            throw new IllegalArgumentException("index greater than the number of items.");
+        }
+        super.remove(pos);
+    }
+
+    /**
+     * Sets the value of the <code>lightWeightPopupEnabled</code> property,
+     * which by default is <code>true</code>.
+     * By default, when a look and feel displays a popup,
+     * it can choose to
+     * use a lightweight (all-Java) popup.
+     * Lightweight popup windows are more efficient than heavyweight
+     * (native peer) windows,
+     * but lightweight and heavyweight components do not mix well in a GUI.
+     * If your application mixes lightweight and heavyweight components,
+     * you should disable lightweight popups.
+     * Some look and feels might always use heavyweight popups,
+     * no matter what the value of this property.
+     *
+     * @param aFlag  <code>false</code> to disable lightweight popups
+     * @beaninfo
+     * description: Determines whether lightweight popups are used when possible
+     *      expert: true
+     *
+     * @see #isLightWeightPopupEnabled
+     */
+    public void setLightWeightPopupEnabled(boolean aFlag) {
+        // NOTE: this use to set the flag on a shared JPopupMenu, which meant
+        // this effected ALL JPopupMenus.
+        lightWeightPopup = aFlag;
+    }
+
+    /**
+     * Gets the <code>lightWeightPopupEnabled</code> property.
+     *
+     * @return the value of the <code>lightWeightPopupEnabled</code> property
+     * @see #setLightWeightPopupEnabled
+     */
+    public boolean isLightWeightPopupEnabled() {
+        return lightWeightPopup;
+    }
+
+    /**
+     * Returns the popup menu's label
+     *
+     * @return a string containing the popup menu's label
+     * @see #setLabel
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Sets the popup menu's label.  Different look and feels may choose
+     * to display or not display this.
+     *
+     * @param label a string specifying the label for the popup menu
+     *
+     * @see #setLabel
+     * @beaninfo
+     * description: The label for the popup menu.
+     *       bound: true
+     */
+    public void setLabel(String label) {
+        String oldValue = this.label;
+        this.label = label;
+        firePropertyChange("label", oldValue, label);
+        if (accessibleContext != null) {
+            accessibleContext.firePropertyChange(
+                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+                oldValue, label);
+        }
+        invalidate();
+        repaint();
+    }
+
+    /**
+     * Appends a new separator at the end of the menu.
+     */
+    public void addSeparator() {
+        add( new JPopupMenu.Separator() );
+    }
+
+    /**
+     * Inserts a menu item for the specified <code>Action</code> object at
+     * a given position.
+     *
+     * @param a  the <code>Action</code> object to insert
+     * @param index      specifies the position at which to insert the
+     *                   <code>Action</code>, where 0 is the first
+     * @exception IllegalArgumentException if <code>index</code> < 0
+     * @see Action
+     */
+    public void insert(Action a, int index) {
+        JMenuItem mi = createActionComponent(a);
+        mi.setAction(a);
+        insert(mi, index);
+    }
+
+    /**
+     * Inserts the specified component into the menu at a given
+     * position.
+     *
+     * @param component  the <code>Component</code> to insert
+     * @param index      specifies the position at which
+     *                   to insert the component, where 0 is the first
+     * @exception IllegalArgumentException if <code>index</code> < 0
+     */
+    public void insert(Component component, int index) {
+        if (index < 0) {
+            throw new IllegalArgumentException("index less than zero.");
+        }
+
+        int nitems = getComponentCount();
+        // PENDING(ges): Why not use an array?
+        Vector tempItems = new Vector();
+
+        /* Remove the item at index, nitems-index times
+           storing them in a temporary vector in the
+           order they appear on the menu.
+           */
+        for (int i = index ; i < nitems; i++) {
+            tempItems.addElement(getComponent(index));
+            remove(index);
+        }
+
+        add(component);
+
+        /* Add the removed items back to the menu, they are
+           already in the correct order in the temp vector.
+           */
+        for (int i = 0; i < tempItems.size()  ; i++) {
+            add((Component)tempItems.elementAt(i));
+        }
+    }
+
+    /**
+     *  Adds a <code>PopupMenu</code> listener.
+     *
+     *  @param l  the <code>PopupMenuListener</code> to add
+     */
+    public void addPopupMenuListener(PopupMenuListener l) {
+        listenerList.add(PopupMenuListener.class,l);
+    }
+
+    /**
+     * Removes a <code>PopupMenu</code> listener.
+     *
+     * @param l  the <code>PopupMenuListener</code> to remove
+     */
+    public void removePopupMenuListener(PopupMenuListener l) {
+        listenerList.remove(PopupMenuListener.class,l);
+    }
+
+    /**
+     * Returns an array of all the <code>PopupMenuListener</code>s added
+     * to this JMenuItem with addPopupMenuListener().
+     *
+     * @return all of the <code>PopupMenuListener</code>s added or an empty
+     *         array if no listeners have been added
+     * @since 1.4
+     */
+    public PopupMenuListener[] getPopupMenuListeners() {
+        return (PopupMenuListener[])listenerList.getListeners(
+                PopupMenuListener.class);
+    }
+
+    /**
+     * Adds a <code>MenuKeyListener</code> to the popup menu.
+     *
+     * @param l the <code>MenuKeyListener</code> to be added
+     * @since 1.5
+     */
+    public void addMenuKeyListener(MenuKeyListener l) {
+        listenerList.add(MenuKeyListener.class, l);
+    }
+
+    /**
+     * Removes a <code>MenuKeyListener</code> from the popup menu.
+     *
+     * @param l the <code>MenuKeyListener</code> to be removed
+     * @since 1.5
+     */
+    public void removeMenuKeyListener(MenuKeyListener l) {
+        listenerList.remove(MenuKeyListener.class, l);
+    }
+
+    /**
+     * Returns an array of all the <code>MenuKeyListener</code>s added
+     * to this JPopupMenu with addMenuKeyListener().
+     *
+     * @return all of the <code>MenuKeyListener</code>s added or an empty
+     *         array if no listeners have been added
+     * @since 1.5
+     */
+    public MenuKeyListener[] getMenuKeyListeners() {
+        return (MenuKeyListener[])listenerList.getListeners(
+                MenuKeyListener.class);
+    }
+
+    /**
+     * Notifies <code>PopupMenuListener</code>s that this popup menu will
+     * become visible.
+     */
+    protected void firePopupMenuWillBecomeVisible() {
+        Object[] listeners = listenerList.getListenerList();
+        PopupMenuEvent e=null;
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==PopupMenuListener.class) {
+                if (e == null)
+                    e = new PopupMenuEvent(this);
+                ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
+            }
+        }
+    }
+
+    /**
+     * Notifies <code>PopupMenuListener</code>s that this popup menu will
+     * become invisible.
+     */
+    protected void firePopupMenuWillBecomeInvisible() {
+        Object[] listeners = listenerList.getListenerList();
+        PopupMenuEvent e=null;
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==PopupMenuListener.class) {
+                if (e == null)
+                    e = new PopupMenuEvent(this);
+                ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
+            }
+        }
+    }
+
+    /**
+     * Notifies <code>PopupMenuListeners</code> that this popup menu is
+     * cancelled.
+     */
+    protected void firePopupMenuCanceled() {
+        Object[] listeners = listenerList.getListenerList();
+        PopupMenuEvent e=null;
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==PopupMenuListener.class) {
+                if (e == null)
+                    e = new PopupMenuEvent(this);
+                ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
+            }
+        }
+    }
+
+    /**
+     * Always returns true since popups, by definition, should always
+     * be on top of all other windows.
+     * @return true
+     */
+    // package private
+    boolean alwaysOnTop() {
+        return true;
+    }
+
+    /**
+     * Lays out the container so that it uses the minimum space
+     * needed to display its contents.
+     */
+    public void pack() {
+        if(popup != null) {
+            Dimension pref = getPreferredSize();
+
+            if (pref == null || pref.width != getWidth() ||
+                                pref.height != getHeight()) {
+                popup = getPopup();
+            } else {
+                validate();
+            }
+        }
+    }
+
+    /**
+     * Sets the visibility of the popup menu.
+     *
+     * @param b true to make the popup visible, or false to
+     *          hide it
+     * @beaninfo
+     *           bound: true
+     *     description: Makes the popup visible
+     */
+    public void setVisible(boolean b) {
+        if (DEBUG) {
+            System.out.println("JPopupMenu.setVisible " + b);
+        }
+
+        // Is it a no-op?
+        if (b == isVisible())
+            return;
+
+        // if closing, first close all Submenus
+        if (b == false) {
+
+            // 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
+            // a protected method and cannot be called from BasicPopupMenuUI directly
+            // The real solution could be to make
+            // firePopupMenuCanceled public and call it directly.
+            Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled");
+            if (doCanceled != null && doCanceled == Boolean.TRUE) {
+                putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
+                firePopupMenuCanceled();
+            }
+            getSelectionModel().clearSelection();
+
+        } else {
+            // This is a popup menu with MenuElement children,
+            // set selection path before popping up!
+            if (isPopupMenu()) {
+                MenuElement me[] = new MenuElement[1];
+                me[0] = (MenuElement) this;
+                MenuSelectionManager.defaultManager().setSelectedPath(me);
+            }
+        }
+
+        if(b) {
+            firePopupMenuWillBecomeVisible();
+            popup = getPopup();
+            firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
+
+
+        } else if(popup != null) {
+            firePopupMenuWillBecomeInvisible();
+            popup.hide();
+            popup = null;
+            firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
+            // 4694797: When popup menu is made invisible, selected path
+            // should be cleared
+            if (isPopupMenu()) {
+                MenuSelectionManager.defaultManager().clearSelectedPath();
+            }
+        }
+    }
+
+    /**
+     * Returns a <code>Popup</code> instance from the
+     * <code>PopupMenuUI</code> that has had <code>show</code> invoked on
+     * it. If the current <code>popup</code> is non-null,
+     * this will invoke <code>dispose</code> of it, and then
+     * <code>show</code> the new one.
+     * <p>
+     * This does NOT fire any events, it is up the caller to dispatch
+     * the necessary events.
+     */
+    private Popup getPopup() {
+        Popup oldPopup = popup;
+
+        if (oldPopup != null) {
+            oldPopup.hide();
+        }
+        PopupFactory popupFactory = PopupFactory.getSharedInstance();
+
+        if (isLightWeightPopupEnabled()) {
+            popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
+        }
+        else {
+            popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
+        }
+
+        // adjust the location of the popup
+        Point p = adjustPopupLocationToFitScreen(desiredLocationX,desiredLocationY);
+        desiredLocationX = p.x;
+        desiredLocationY = p.y;
+
+        Popup newPopup = getUI().getPopup(this, desiredLocationX,
+                                          desiredLocationY);
+
+        popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
+        newPopup.show();
+        return newPopup;
+    }
+
+    /**
+     * Returns true if the popup menu is visible (currently
+     * being displayed).
+     */
+    public boolean isVisible() {
+        if(popup != null)
+            return true;
+        else
+            return false;
+    }
+
+    /**
+     * Sets the location of the upper left corner of the
+     * popup menu using x, y coordinates.
+     *
+     * @param x the x coordinate of the popup's new position
+     *          in the screen's coordinate space
+     * @param y the y coordinate of the popup's new position
+     *          in the screen's coordinate space
+     * @beaninfo
+     * description: The location of the popup menu.
+     */
+    public void setLocation(int x, int y) {
+        int oldX = desiredLocationX;
+        int oldY = desiredLocationY;
+
+        desiredLocationX = x;
+        desiredLocationY = y;
+        if(popup != null && (x != oldX || y != oldY)) {
+            popup = getPopup();
+        }
+    }
+
+    /**
+     * Returns true if the popup menu is a standalone popup menu
+     * rather than the submenu of a <code>JMenu</code>.
+     *
+     * @return true if this menu is a standalone popup menu, otherwise false
+     */
+    private boolean isPopupMenu() {
+        return  ((invoker != null) && !(invoker instanceof JMenu));
+    }
+
+    /**
+     * Returns the component which is the 'invoker' of this
+     * popup menu.
+     *
+     * @return the <code>Component</code> in which the popup menu is displayed
+     */
+    public Component getInvoker() {
+        return this.invoker;
+    }
+
+    /**
+     * Sets the invoker of this popup menu -- the component in which
+     * the popup menu menu is to be displayed.
+     *
+     * @param invoker the <code>Component</code> in which the popup
+     *          menu is displayed
+     * @beaninfo
+     * description: The invoking component for the popup menu
+     *      expert: true
+     */
+    public void setInvoker(Component invoker) {
+        Component oldInvoker = this.invoker;
+        this.invoker = invoker;
+        if ((oldInvoker != this.invoker) && (ui != null)) {
+            ui.uninstallUI(this);
+            ui.installUI(this);
+        }
+        invalidate();
+    }
+
+    /**
+     * Displays the popup menu at the position x,y in the coordinate
+     * space of the component invoker.
+     *
+     * @param invoker the component in whose space the popup menu is to appear
+     * @param x the x coordinate in invoker's coordinate space at which
+     * the popup menu is to be displayed
+     * @param y the y coordinate in invoker's coordinate space at which
+     * the popup menu is to be displayed
+     */
+    public void show(Component invoker, int x, int y) {
+        if (DEBUG) {
+            System.out.println("in JPopupMenu.show " );
+        }
+        setInvoker(invoker);
+        Frame newFrame = getFrame(invoker);
+        if (newFrame != frame) {
+            // Use the invoker's frame so that events
+            // are propagated properly
+            if (newFrame!=null) {
+                this.frame = newFrame;
+                if(popup != null) {
+                    setVisible(false);
+                }
+            }
+        }
+        Point invokerOrigin;
+        if (invoker != null) {
+            invokerOrigin = invoker.getLocationOnScreen();
+
+            // To avoid integer overflow
+            long lx, ly;
+            lx = ((long) invokerOrigin.x) +
+                 ((long) x);
+            ly = ((long) invokerOrigin.y) +
+                 ((long) y);
+            if(lx > Integer.MAX_VALUE) lx = Integer.MAX_VALUE;
+            if(lx < Integer.MIN_VALUE) lx = Integer.MIN_VALUE;
+            if(ly > Integer.MAX_VALUE) ly = Integer.MAX_VALUE;
+            if(ly < Integer.MIN_VALUE) ly = Integer.MIN_VALUE;
+
+            setLocation((int) lx, (int) ly);
+        } else {
+            setLocation(x, y);
+        }
+        setVisible(true);
+    }
+
+    /**
+     * Returns the popup menu which is at the root of the menu system
+     * for this popup menu.
+     *
+     * @return the topmost grandparent <code>JPopupMenu</code>
+     */
+    JPopupMenu getRootPopupMenu() {
+        JPopupMenu mp = this;
+        while((mp!=null) && (mp.isPopupMenu()!=true) &&
+              (mp.getInvoker() != null) &&
+              (mp.getInvoker().getParent() != null) &&
+              (mp.getInvoker().getParent() instanceof JPopupMenu)
+              ) {
+            mp = (JPopupMenu) mp.getInvoker().getParent();
+        }
+        return mp;
+    }
+
+    /**
+     * Returns the component at the specified index.
+     *
+     * @param i  the index of the component, where 0 is the first
+     * @return the <code>Component</code> at that index
+     * @deprecated replaced by {@link java.awt.Container#getComponent(int)}
+     */
+    @Deprecated
+    public Component getComponentAtIndex(int i) {
+        return getComponent(i);
+    }
+
+    /**
+     * Returns the index of the specified component.
+     *
+     * @param  c the <code>Component</code> to find
+     * @return the index of the component, where 0 is the first;
+     *         or -1 if the component is not found
+     */
+    public int getComponentIndex(Component c) {
+        int ncomponents = this.getComponentCount();
+        Component[] component = this.getComponents();
+        for (int i = 0 ; i < ncomponents ; i++) {
+            Component comp = component[i];
+            if (comp == c)
+                return i;
+        }
+        return -1;
+    }
+
+    /**
+     * Sets the size of the Popup window using a <code>Dimension</code> object.
+     * This is equivalent to <code>setPreferredSize(d)</code>.
+     *
+     * @param d   the <code>Dimension</code> specifying the new size
+     * of this component.
+     * @beaninfo
+     * description: The size of the popup menu
+     */
+    public void setPopupSize(Dimension d) {
+        Dimension oldSize = getPreferredSize();
+
+        setPreferredSize(d);
+        if (popup != null) {
+            Dimension newSize = getPreferredSize();
+
+            if (!oldSize.equals(newSize)) {
+                popup = getPopup();
+            }
+        }
+    }
+
+    /**
+     * Sets the size of the Popup window to the specified width and
+     * height. This is equivalent to
+     *  <code>setPreferredSize(new Dimension(width, height))</code>.
+     *
+     * @param width the new width of the Popup in pixels
+     * @param height the new height of the Popup in pixels
+     * @beaninfo
+     * description: The size of the popup menu
+     */
+    public void setPopupSize(int width, int height) {
+        setPopupSize(new Dimension(width, height));
+    }
+
+    /**
+     * Sets the currently selected component,  This will result
+     * in a change to the selection model.
+     *
+     * @param sel the <code>Component</code> to select
+     * @beaninfo
+     * description: The selected component on the popup menu
+     *      expert: true
+     *      hidden: true
+     */
+    public void setSelected(Component sel) {
+        SingleSelectionModel model = getSelectionModel();
+        int index = getComponentIndex(sel);
+        model.setSelectedIndex(index);
+    }
+
+    /**
+     * Checks whether the border should be painted.
+     *
+     * @return true if the border is painted, false otherwise
+     * @see #setBorderPainted
+     */
+    public boolean isBorderPainted() {
+        return paintBorder;
+    }
+
+    /**
+     * Sets whether the border should be painted.
+     *
+     * @param b if true, the border is painted.
+     * @see #isBorderPainted
+     * @beaninfo
+     * description: Is the border of the popup menu painted
+     */
+    public void setBorderPainted(boolean b) {
+        paintBorder = b;
+        repaint();
+    }
+
+    /**
+     * Paints the popup menu's border if the <code>borderPainted</code>
+     * property is <code>true</code>.
+     * @param g  the <code>Graphics</code> object
+     *
+     * @see JComponent#paint
+     * @see JComponent#setBorder
+     */
+    protected void paintBorder(Graphics g) {
+        if (isBorderPainted()) {
+            super.paintBorder(g);
+        }
+    }
+
+    /**
+     * Returns the margin, in pixels, between the popup menu's border and
+     * its containees.
+     *
+     * @return an <code>Insets</code> object containing the margin values.
+     */
+    public Insets getMargin() {
+        if(margin == null) {
+            return new Insets(0,0,0,0);
+        } else {
+            return margin;
+        }
+    }
+
+
+    /**
+     * Examines the list of menu items to determine whether
+     * <code>popup</code> is a popup menu.
+     *
+     * @param popup  a <code>JPopupMenu</code>
+     * @return true if <code>popup</code>
+     */
+    boolean isSubPopupMenu(JPopupMenu popup) {
+        int ncomponents = this.getComponentCount();
+        Component[] component = this.getComponents();
+        for (int i = 0 ; i < ncomponents ; i++) {
+            Component comp = component[i];
+            if (comp instanceof JMenu) {
+                JMenu menu = (JMenu)comp;
+                JPopupMenu subPopup = menu.getPopupMenu();
+                if (subPopup == popup)
+                    return true;
+                if (subPopup.isSubPopupMenu(popup))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+
+    private static Frame getFrame(Component c) {
+        Component w = c;
+
+        while(!(w instanceof Frame) && (w!=null)) {
+            w = w.getParent();
+        }
+        return (Frame)w;
+    }
+
+
+    /**
+     * Returns a string representation of this <code>JPopupMenu</code>.
+     * This method
+     * is intended to be used only for debugging purposes, and the
+     * content and format of the returned string may vary between
+     * implementations. The returned string may be empty but may not
+     * be <code>null</code>.
+     *
+     * @return  a string representation of this <code>JPopupMenu</code>.
+     */
+    protected String paramString() {
+        String labelString = (label != null ?
+                              label : "");
+        String paintBorderString = (paintBorder ?
+                                    "true" : "false");
+        String marginString = (margin != null ?
+                              margin.toString() : "");
+        String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ?
+                                                "true" : "false");
+        return super.paramString() +
+            ",desiredLocationX=" + desiredLocationX +
+            ",desiredLocationY=" + desiredLocationY +
+        ",label=" + labelString +
+        ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
+        ",margin=" + marginString +
+        ",paintBorder=" + paintBorderString;
+    }
+
+/////////////////
+// Accessibility support
+////////////////
+
+    /**
+     * Gets the AccessibleContext associated with this JPopupMenu.
+     * For JPopupMenus, the AccessibleContext takes the form of an
+     * AccessibleJPopupMenu.
+     * A new AccessibleJPopupMenu instance is created if necessary.
+     *
+     * @return an AccessibleJPopupMenu that serves as the
+     *         AccessibleContext of this JPopupMenu
+     */
+    public AccessibleContext getAccessibleContext() {
+        if (accessibleContext == null) {
+            accessibleContext = new AccessibleJPopupMenu();
+        }
+        return accessibleContext;
+    }
+
+    /**
+     * This class implements accessibility support for the
+     * <code>JPopupMenu</code> class.  It provides an implementation of the
+     * Java Accessibility API appropriate to popup menu user-interface
+     * elements.
+     */
+    protected class AccessibleJPopupMenu extends AccessibleJComponent
+        implements PropertyChangeListener {
+
+        /**
+         * AccessibleJPopupMenu constructor
+         *
+         * @since 1.5
+         */
+        protected AccessibleJPopupMenu() {
+            JPopupMenu.this.addPropertyChangeListener(this);
+        }
+
+        /**
+         * Get the role of this object.
+         *
+         * @return an instance of AccessibleRole describing the role of
+         * the object
+         */
+        public AccessibleRole getAccessibleRole() {
+            return AccessibleRole.POPUP_MENU;
+        }
+
+        /**
+         * This method gets called when a bound property is changed.
+         * @param e A <code>PropertyChangeEvent</code> object describing
+         * the event source and the property that has changed. Must not be null.
+         *
+         * @throws NullPointerException if the parameter is null.
+         * @since 1.5
+         */
+        public void propertyChange(PropertyChangeEvent e) {
+            String propertyName = e.getPropertyName();
+            if (propertyName == "visible") {
+                if (e.getOldValue() == Boolean.FALSE &&
+                    e.getNewValue() == Boolean.TRUE) {
+                    handlePopupIsVisibleEvent(true);
+
+                } else if (e.getOldValue() == Boolean.TRUE &&
+                           e.getNewValue() == Boolean.FALSE) {
+                    handlePopupIsVisibleEvent(false);
+                }
+            }
+        }
+
+        /*
+         * Handles popup "visible" PropertyChangeEvent
+         */
+        private void handlePopupIsVisibleEvent(boolean visible) {
+            if (visible) {
+                // notify listeners that the popup became visible
+                firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
+                                   null, AccessibleState.VISIBLE);
+                // notify listeners that a popup list item is selected
+                fireActiveDescendant();
+            } else {
+                // notify listeners that the popup became hidden
+                firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
+                                   AccessibleState.VISIBLE, null);
+            }
+        }
+
+        /*
+         * Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners
+         * on the popup menu invoker that a popup list item has been selected
+         */
+        private void fireActiveDescendant() {
+            if (JPopupMenu.this instanceof BasicComboPopup) {
+                // get the popup list
+                JList popupList = ((BasicComboPopup)JPopupMenu.this).getList();
+                if (popupList == null) {
+                    return;
+                }
+
+                // get the first selected item
+                AccessibleContext ac = popupList.getAccessibleContext();
+                AccessibleSelection selection = ac.getAccessibleSelection();
+                if (selection == null) {
+                    return;
+                }
+                Accessible a = selection.getAccessibleSelection(0);
+                if (a == null) {
+                    return;
+                }
+                AccessibleContext selectedItem = a.getAccessibleContext();
+
+                // fire the event with the popup invoker as the source.
+                if (selectedItem != null && invoker != null) {
+                    AccessibleContext invokerContext = invoker.getAccessibleContext();
+                    if (invokerContext != null) {
+                        // Check invokerContext because Component.getAccessibleContext
+                        // returns null. Classes that extend Component are responsible
+                        // for returning a non-null AccessibleContext.
+                        invokerContext.firePropertyChange(
+                            ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
+                            null, selectedItem);
+                    }
+                }
+            }
+        }
+    } // inner class AccessibleJPopupMenu
+
+
+////////////
+// Serialization support.
+////////////
+    private void writeObject(ObjectOutputStream s) throws IOException {
+        Vector      values = new Vector();
+
+        s.defaultWriteObject();
+        // Save the invoker, if its Serializable.
+        if(invoker != null && invoker instanceof Serializable) {
+            values.addElement("invoker");
+            values.addElement(invoker);
+        }
+        // Save the popup, if its Serializable.
+        if(popup != null && popup instanceof Serializable) {
+            values.addElement("popup");
+            values.addElement(popup);
+        }
+        s.writeObject(values);
+
+        if (getUIClassID().equals(uiClassID)) {
+            byte count = JComponent.getWriteObjCounter(this);
+            JComponent.setWriteObjCounter(this, --count);
+            if (count == 0 && ui != null) {
+                ui.installUI(this);
+            }
+        }
+    }
+
+    // implements javax.swing.MenuElement
+    private void readObject(ObjectInputStream s)
+        throws IOException, ClassNotFoundException {
+        s.defaultReadObject();
+
+        Vector          values = (Vector)s.readObject();
+        int             indexCounter = 0;
+        int             maxCounter = values.size();
+
+        if(indexCounter < maxCounter && values.elementAt(indexCounter).
+           equals("invoker")) {
+            invoker = (Component)values.elementAt(++indexCounter);
+            indexCounter++;
+        }
+        if(indexCounter < maxCounter && values.elementAt(indexCounter).
+           equals("popup")) {
+            popup = (Popup)values.elementAt(++indexCounter);
+            indexCounter++;
+        }
+    }
+
+
+    /**
+     * This method is required to conform to the
+     * <code>MenuElement</code> interface, but it not implemented.
+     * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)
+     */
+    public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {}
+
+    /**
+     * Processes a key event forwarded from the
+     * <code>MenuSelectionManager</code> and changes the menu selection,
+     * if necessary, by using <code>MenuSelectionManager</code>'s API.
+     * <p>
+     * Note: you do not have to forward the event to sub-components.
+     * This is done automatically by the <code>MenuSelectionManager</code>.
+     *
+     * @param e  a <code>KeyEvent</code>
+     * @param path the <code>MenuElement</code> path array
+     * @param manager   the <code>MenuSelectionManager</code>
+     */
+    public void processKeyEvent(KeyEvent e, MenuElement path[],
+                                MenuSelectionManager manager) {
+        MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(),
+                                             e.getWhen(), e.getModifiers(),
+                                             e.getKeyCode(), e.getKeyChar(),
+                                             path, manager);
+        processMenuKeyEvent(mke);
+
+        if (mke.isConsumed())  {
+            e.consume();
+    }
+    }
+
+    /**
+     * Handles a keystroke in a menu.
+     *
+     * @param e  a <code>MenuKeyEvent</code> object
+     * @since 1.5
+     */
+    private void processMenuKeyEvent(MenuKeyEvent e) {
+        switch (e.getID()) {
+        case KeyEvent.KEY_PRESSED:
+            fireMenuKeyPressed(e); break;
+        case KeyEvent.KEY_RELEASED:
+            fireMenuKeyReleased(e); break;
+        case KeyEvent.KEY_TYPED:
+            fireMenuKeyTyped(e); break;
+        default:
+            break;
+        }
+    }
+
+    /**
+     * Notifies all listeners that have registered interest for
+     * notification on this event type.
+     *
+     * @param event a <code>MenuKeyEvent</code>
+     * @see EventListenerList
+     */
+    private void fireMenuKeyPressed(MenuKeyEvent event) {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==MenuKeyListener.class) {
+                ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event);
+            }
+        }
+    }
+
+    /**
+     * Notifies all listeners that have registered interest for
+     * notification on this event type.
+     *
+     * @param event a <code>MenuKeyEvent</code>
+     * @see EventListenerList
+     */
+    private void fireMenuKeyReleased(MenuKeyEvent event) {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==MenuKeyListener.class) {
+                ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event);
+            }
+        }
+    }
+
+    /**
+     * Notifies all listeners that have registered interest for
+     * notification on this event type.
+     *
+     * @param event a <code>MenuKeyEvent</code>
+     * @see EventListenerList
+     */
+    private void fireMenuKeyTyped(MenuKeyEvent event) {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==MenuKeyListener.class) {
+                ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event);
+            }
+        }
+    }
+
+    /**
+     * Messaged when the menubar selection changes to activate or
+     * deactivate this menu. This implements the
+     * <code>javax.swing.MenuElement</code> interface.
+     * Overrides <code>MenuElement.menuSelectionChanged</code>.
+     *
+     * @param isIncluded  true if this menu is active, false if
+     *        it is not
+     * @see MenuElement#menuSelectionChanged(boolean)
+     */
+    public void menuSelectionChanged(boolean isIncluded) {
+        if (DEBUG) {
+            System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded);
+        }
+        if(invoker instanceof JMenu) {
+            JMenu m = (JMenu) invoker;
+            if(isIncluded)
+                m.setPopupMenuVisible(true);
+            else
+                m.setPopupMenuVisible(false);
+        }
+        if (isPopupMenu() && !isIncluded)
+          setVisible(false);
+    }
+
+    /**
+     * Returns an array of <code>MenuElement</code>s containing the submenu
+     * for this menu component.  It will only return items conforming to
+     * the <code>JMenuElement</code> interface.
+     * If popup menu is <code>null</code> returns
+     * an empty array.  This method is required to conform to the
+     * <code>MenuElement</code> interface.
+     *
+     * @return an array of <code>MenuElement</code> objects
+     * @see MenuElement#getSubElements
+     */
+    public MenuElement[] getSubElements() {
+        MenuElement result[];
+        Vector tmp = new Vector();
+        int c = getComponentCount();
+        int i;
+        Component m;
+
+        for(i=0 ; i < c ; i++) {
+            m = getComponent(i);
+            if(m instanceof MenuElement)
+                tmp.addElement(m);
+        }
+
+        result = new MenuElement[tmp.size()];
+        for(i=0,c=tmp.size() ; i < c ; i++)
+            result[i] = (MenuElement) tmp.elementAt(i);
+        return result;
+    }
+
+    /**
+     * Returns this <code>JPopupMenu</code> component.
+     * @return this <code>JPopupMenu</code> object
+     * @see MenuElement#getComponent
+     */
+    public Component getComponent() {
+        return this;
+    }
+
+
+    /**
+     * A popup menu-specific separator.
+     */
+    static public class Separator extends JSeparator
+    {
+        public Separator( )
+        {
+            super( JSeparator.HORIZONTAL );
+        }
+
+        /**
+         * Returns the name of the L&F class that renders this component.
+         *
+         * @return the string "PopupMenuSeparatorUI"
+         * @see JComponent#getUIClassID
+         * @see UIDefaults#getUI
+         */
+        public String getUIClassID()
+        {
+            return "PopupMenuSeparatorUI";
+
+        }
+    }
+
+    /**
+     * Returns true if the <code>MouseEvent</code> is considered a popup trigger
+     * by the <code>JPopupMenu</code>'s currently installed UI.
+     *
+     * @return true if the mouse event is a popup trigger
+     * @since 1.3
+     */
+    public boolean isPopupTrigger(MouseEvent e) {
+        return getUI().isPopupTrigger(e);
+    }
+}