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