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