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