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