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