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