KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > JPopupMenu


1 /*
2  * @(#)JPopupMenu.java 1.191 04/05/18
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing;
9
10 import java.awt.*;
11 import java.awt.event.*;
12 import java.io.IOException JavaDoc;
13 import java.io.ObjectInputStream JavaDoc;
14 import java.io.ObjectOutputStream JavaDoc;
15 import java.io.Serializable JavaDoc;
16 import java.beans.*;
17
18 import java.util.Locale JavaDoc;
19 import java.util.Vector JavaDoc;
20 import java.util.Hashtable JavaDoc;
21 import javax.accessibility.*;
22 import javax.swing.plaf.PopupMenuUI JavaDoc;
23 import javax.swing.plaf.ComponentUI JavaDoc;
24 import javax.swing.plaf.basic.BasicComboPopup JavaDoc;
25 import javax.swing.event.*;
26
27 import java.applet.Applet JavaDoc;
28
29 /**
30  * An implementation of a popup menu -- a small window that pops up
31  * and displays a series of choices. A <code>JPopupMenu</code> is used for the
32  * menu that appears when the user selects an item on the menu bar.
33  * It is also used for "pull-right" menu that appears when the
34  * selects a menu item that activates it. Finally, a <code>JPopupMenu</code>
35  * can also be used anywhere else you want a menu to appear. For
36  * example, when the user right-clicks in a specified area.
37  * <p>
38  * For information and examples of using popup menus, see
39  * <a
40  href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
41  * in <em>The Java Tutorial.</em>
42  * <p>
43  * <strong>Warning:</strong>
44  * Serialized objects of this class will not be compatible with
45  * future Swing releases. The current serialization support is
46  * appropriate for short term storage or RMI between applications running
47  * the same version of Swing. As of 1.4, support for long term storage
48  * of all JavaBeans<sup><font size="-2">TM</font></sup>
49  * has been added to the <code>java.beans</code> package.
50  * Please see {@link java.beans.XMLEncoder}.
51  *
52  * @beaninfo
53  * attribute: isContainer false
54  * description: A small window that pops up and displays a series of choices.
55  *
56  * @version 1.191 @(#)JPopupMenu.java 1.191
57  * @author Georges Saab
58  * @author David Karlton
59  * @author Arnaud Weber
60  */

61 public class JPopupMenu extends JComponent JavaDoc implements Accessible,MenuElement JavaDoc {
62
63     /**
64      * @see #getUIClassID
65      * @see #readObject
66      */

67     private static final String JavaDoc uiClassID = "PopupMenuUI";
68
69     /**
70      * Key used in AppContext to determine if light way popups are the default.
71      */

72     private static final Object JavaDoc defaultLWPopupEnabledKey =
73         new StringBuffer JavaDoc("JPopupMenu.defaultLWPopupEnabledKey");
74
75     /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
76     static boolean popupPostionFixDisabled = false;
77
78     static {
79         popupPostionFixDisabled = java.security.AccessController.doPrivileged(
80                 new sun.security.action.GetPropertyAction(
81                 "javax.swing.adjustPopupLocationToFit","")).equals("false");
82
83     }
84
85     transient Component invoker;
86     transient Popup JavaDoc popup;
87     transient Frame frame;
88     private int desiredLocationX,desiredLocationY;
89
90     private String JavaDoc label = null;
91     private boolean paintBorder = true;
92     private Insets margin = null;
93
94     /**
95      * Used to indicate if lightweight popups should be used.
96      */

97     private boolean lightWeightPopup = true;
98
99     /*
100      * Model for the selected subcontrol.
101      */

102     private SingleSelectionModel JavaDoc selectionModel;
103
104     /* Lock object used in place of class object for synchronization.
105      * (4187686)
106      */

107     private static final Object JavaDoc classLock = new Object JavaDoc();
108
109     /* diagnostic aids -- should be false for production builds. */
110     private static final boolean TRACE = false; // trace creates and disposes
111
private static final boolean VERBOSE = false; // show reuse hits/misses
112
private static final boolean DEBUG = false; // show bad params, misc.
113

114     /**
115      * Sets the default value of the <code>lightWeightPopupEnabled</code>
116      * property.
117      *
118      * @param aFlag <code>true</code> if popups can be lightweight,
119      * otherwise <code>false</code>
120      * @see #getDefaultLightWeightPopupEnabled
121      * @see #setLightWeightPopupEnabled
122      */

123     public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
124         SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
125                                      Boolean.valueOf(aFlag));
126     }
127
128     /**
129      * Gets the <code>defaultLightWeightPopupEnabled</code> property,
130      * which by default is <code>true</code>.
131      *
132      * @return the value of the <code>defaultLightWeightPopupEnabled</code>
133      * property
134      *
135      * @see #setDefaultLightWeightPopupEnabled
136      */

137     public static boolean getDefaultLightWeightPopupEnabled() {
138         Boolean JavaDoc b = (Boolean JavaDoc)
139             SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
140         if (b == null) {
141             SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
142                                          Boolean.TRUE);
143             return true;
144         }
145         return b.booleanValue();
146     }
147
148     /**
149      * Constructs a <code>JPopupMenu</code> without an "invoker".
150      */

151     public JPopupMenu() {
152         this(null);
153     }
154
155     /**
156      * Constructs a <code>JPopupMenu</code> with the specified title.
157      *
158      * @param label the string that a UI may use to display as a title
159      * for the popup menu.
160      */

161     public JPopupMenu(String JavaDoc label) {
162         this.label = label;
163         lightWeightPopup = getDefaultLightWeightPopupEnabled();
164         setSelectionModel(new DefaultSingleSelectionModel JavaDoc());
165         enableEvents(AWTEvent.MOUSE_EVENT_MASK);
166     setFocusTraversalKeysEnabled(false);
167         updateUI();
168     }
169
170
171
172     /**
173      * Returns the look and feel (L&F) object that renders this component.
174      *
175      * @return the <code>PopupMenuUI</code> object that renders this component
176      */

177     public PopupMenuUI JavaDoc getUI() {
178         return (PopupMenuUI JavaDoc)ui;
179     }
180     
181     /**
182      * Sets the L&F object that renders this component.
183      *
184      * @param ui the new <code>PopupMenuUI</code> L&F object
185      * @see UIDefaults#getUI
186      * @beaninfo
187      * bound: true
188      * hidden: true
189      * attribute: visualUpdate true
190      * description: The UI object that implements the Component's LookAndFeel.
191      */

192     public void setUI(PopupMenuUI JavaDoc ui) {
193         super.setUI(ui);
194     }
195     
196     /**
197      * Resets the UI property to a value from the current look and feel.
198      *
199      * @see JComponent#updateUI
200      */

201     public void updateUI() {
202         setUI((PopupMenuUI JavaDoc)UIManager.getUI(this));
203     }
204
205
206     /**
207      * Returns the name of the L&F class that renders this component.
208      *
209      * @return the string "PopupMenuUI"
210      * @see JComponent#getUIClassID
211      * @see UIDefaults#getUI
212      */

213     public String JavaDoc getUIClassID() {
214         return uiClassID;
215     }
216
217     protected void processFocusEvent(FocusEvent evt) {
218     super.processFocusEvent(evt);
219     }
220
221     /**
222      * Processes key stroke events such as mnemonics and accelerators.
223      *
224      * @param evt the key event to be processed
225      */

226     protected void processKeyEvent(KeyEvent evt) {
227         MenuSelectionManager.defaultManager().processKeyEvent(evt);
228     if (evt.isConsumed()) {
229         return;
230     }
231     super.processKeyEvent(evt);
232     }
233
234
235     /**
236      * Returns the model object that handles single selections.
237      *
238      * @return the <code>selectionModel</code> property
239      * @see SingleSelectionModel
240      */

241     public SingleSelectionModel JavaDoc getSelectionModel() {
242         return selectionModel;
243     }
244
245     /**
246      * Sets the model object to handle single selections.
247      *
248      * @param model the new <code>SingleSelectionModel</code>
249      * @see SingleSelectionModel
250      * @beaninfo
251      * description: The selection model for the popup menu
252      * expert: true
253      */

254     public void setSelectionModel(SingleSelectionModel JavaDoc model) {
255         selectionModel = model;
256     }
257
258     /**
259      * Appends the specified menu item to the end of this menu.
260      *
261      * @param menuItem the <code>JMenuItem</code> to add
262      * @return the <code>JMenuItem</code> added
263      */

264     public JMenuItem JavaDoc add(JMenuItem JavaDoc menuItem) {
265         super.add(menuItem);
266         return menuItem;
267     }
268
269     /**
270      * Creates a new menu item with the specified text and appends
271      * it to the end of this menu.
272      *
273      * @param s the string for the menu item to be added
274      */

275     public JMenuItem JavaDoc add(String JavaDoc s) {
276         return add(new JMenuItem JavaDoc(s));
277     }
278
279     /**
280      * Appends a new menu item to the end of the menu which
281      * dispatches the specified <code>Action</code> object.
282      *
283      * As of JDK 1.3, this is no longer the preferred method for adding
284      * <code>Actions</code> to
285      * a container. Instead it is recommended to configure a control with
286      * an action using <code>setAction</code>, and then add that control
287      * directly to the <code>Container</code>.
288      *
289      * @param a the <code>Action</code> to add to the menu
290      * @return the new menu item
291      * @see Action
292      */

293     public JMenuItem JavaDoc add(Action JavaDoc a) {
294     JMenuItem JavaDoc mi = createActionComponent(a);
295     mi.setAction(a);
296         add(mi);
297         return mi;
298     }
299     
300     /**
301      * Returns an point which has been adjusted to take into account of the
302      * desktop bounds, taskbar and multi-monitor configuration.
303      * <p>
304      * This adustment may be cancelled by invoking the application with
305      * -Djavax.swing.adjustPopupLocationToFit=false
306      */

307     Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
308     Point p = new Point(xposition, yposition);
309
310         if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
311             return p;
312
313         Toolkit toolkit = Toolkit.getDefaultToolkit();
314         Rectangle screenBounds;
315         Insets screenInsets;
316         GraphicsConfiguration gc = null;
317         // Try to find GraphicsConfiguration, that includes mouse
318
// pointer position
319
GraphicsEnvironment ge =
320             GraphicsEnvironment.getLocalGraphicsEnvironment();
321         GraphicsDevice[] gd = ge.getScreenDevices();
322         for(int i = 0; i < gd.length; i++) {
323             if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
324                 GraphicsConfiguration dgc =
325                     gd[i].getDefaultConfiguration();
326                 if(dgc.getBounds().contains(p)) {
327                     gc = dgc;
328                     break;
329                 }
330             }
331         }
332
333         // If not found and we have invoker, ask invoker about his gc
334
if(gc == null && getInvoker() != null) {
335             gc = getInvoker().getGraphicsConfiguration();
336         }
337
338         if(gc != null) {
339             // If we have GraphicsConfiguration use it to get
340
// screen bounds and insets
341
screenInsets = toolkit.getScreenInsets(gc);
342             screenBounds = gc.getBounds();
343         } else {
344             // If we don't have GraphicsConfiguration use primary screen
345
// and empty insets
346
screenInsets = new Insets(0, 0, 0, 0);
347             screenBounds = new Rectangle(toolkit.getScreenSize());
348         }
349
350         int scrWidth = screenBounds.width -
351                     Math.abs(screenInsets.left+screenInsets.right);
352         int scrHeight = screenBounds.height -
353                     Math.abs(screenInsets.top+screenInsets.bottom);
354
355         Dimension size;
356
357         size = JPopupMenu.this.getPreferredSize();
358
359         if( (p.x + size.width) > screenBounds.x + scrWidth )
360              p.x = screenBounds.x + scrWidth - size.width;
361
362         if( (p.y + size.height) > screenBounds.y + scrHeight)
363              p.y = screenBounds.y + scrHeight - size.height;
364
365         /* Change is made to the desired (X,Y) values, when the
366            PopupMenu is too tall OR too wide for the screen
367         */

368         if( p.x < screenBounds.x )
369             p.x = screenBounds.x ;
370         if( p.y < screenBounds.y )
371             p.y = screenBounds.y;
372
373         return p;
374     }
375
376
377     /**
378      * Factory method which creates the <code>JMenuItem</code> for
379      * <code>Actions</code> added to the <code>JPopupMenu</code>.
380      * As of JDK 1.3, this is no
381      * longer the preferred method, instead it is recommended to configure
382      * a control with an action using <code>setAction</code>,
383      * and then adding that
384      * control directly to the <code>Container</code>.
385      *
386      * @param a the <code>Action</code> for the menu item to be added
387      * @return the new menu item
388      * @see Action
389      *
390      * @since 1.3
391      */

392     protected JMenuItem JavaDoc createActionComponent(Action JavaDoc a) {
393         JMenuItem JavaDoc mi = new JMenuItem JavaDoc((String JavaDoc)a.getValue(Action.NAME),
394                                      (Icon JavaDoc)a.getValue(Action.SMALL_ICON)){
395         protected PropertyChangeListener createActionPropertyChangeListener(Action JavaDoc a) {
396         PropertyChangeListener pcl = createActionChangeListener(this);
397         if (pcl == null) {
398             pcl = super.createActionPropertyChangeListener(a);
399         }
400         return pcl;
401         }
402     };
403         mi.setHorizontalTextPosition(JButton.TRAILING);
404         mi.setVerticalTextPosition(JButton.CENTER);
405         mi.setEnabled(a.isEnabled());
406     return mi;
407     }
408
409     /**
410      * Returns a properly configured <code>PropertyChangeListener</code>
411      * which updates the control as changes to the <code>Action</code> occur.
412      * As of JDK 1.3, this is no longer the preferred method for adding
413      * <code>Actions</code> to
414      * a container. Instead it is recommended to configure a control with
415      * an action using <code>setAction</code>, and then add that control
416      * directly to the <code>Container</code>.
417      */

418     protected PropertyChangeListener createActionChangeListener(JMenuItem JavaDoc b) {
419         return new ActionChangedListener(b);
420     }
421
422     private class ActionChangedListener implements PropertyChangeListener, Serializable JavaDoc {
423         private JMenuItem JavaDoc menuItem;
424         
425         public ActionChangedListener(JMenuItem JavaDoc mi) {
426             super();
427             setTarget(mi);
428         }
429         public void propertyChange(PropertyChangeEvent e) {
430             String JavaDoc propertyName = e.getPropertyName();
431             if (e.getPropertyName().equals(Action.NAME)) {
432                 String JavaDoc text = (String JavaDoc) e.getNewValue();
433                 menuItem.setText(text);
434             } else if (propertyName.equals("enabled")) {
435                 Boolean JavaDoc enabledState = (Boolean JavaDoc) e.getNewValue();
436                 menuItem.setEnabled(enabledState.booleanValue());
437             } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
438                 Icon JavaDoc icon = (Icon JavaDoc) e.getNewValue();
439                 menuItem.setIcon(icon);
440                 menuItem.invalidate();
441                 menuItem.repaint();
442             }
443         }
444     public void setTarget(JMenuItem JavaDoc b) {
445         this.menuItem = b;
446     }
447     }
448
449     /**
450      * Removes the component at the specified index from this popup menu.
451      *
452      * @param pos the position of the item to be removed
453      * @exception IllegalArgumentException if the value of
454      * <code>pos</code> < 0, or if the value of
455      * <code>pos</code> is greater than the
456      * number of items
457      */

458     public void remove(int pos) {
459         if (pos < 0) {
460             throw new IllegalArgumentException JavaDoc("index less than zero.");
461         }
462         if (pos > getComponentCount() -1) {
463             throw new IllegalArgumentException JavaDoc("index greater than the number of items.");
464         }
465     super.remove(pos);
466     }
467
468     /**
469      * Sets the value of the <code>lightWeightPopupEnabled</code> property,
470      * which by default is <code>true</code>.
471      * By default, when a look and feel displays a popup,
472      * it can choose to
473      * use a lightweight (all-Java) popup.
474      * Lightweight popup windows are more efficient than heavyweight
475      * (native peer) windows,
476      * but lightweight and heavyweight components do not mix well in a GUI.
477      * If your application mixes lightweight and heavyweight components,
478      * you should disable lightweight popups.
479      * Some look and feels might always use heavyweight popups,
480      * no matter what the value of this property.
481      *
482      * @param aFlag <code>false</code> to disable lightweight popups
483      * @beaninfo
484      * description: Determines whether lightweight popups are used when possible
485      * expert: true
486      *
487      * @see #isLightWeightPopupEnabled
488      */

489     public void setLightWeightPopupEnabled(boolean aFlag) {
490         // NOTE: this use to set the flag on a shared JPopupMenu, which meant
491
// this effected ALL JPopupMenus.
492
lightWeightPopup = aFlag;
493     }
494
495     /**
496      * Gets the <code>lightWeightPopupEnabled</code> property.
497      *
498      * @return the value of the <code>lightWeightPopupEnabled</code> property
499      * @see #setLightWeightPopupEnabled
500      */

501     public boolean isLightWeightPopupEnabled() {
502         return lightWeightPopup;
503     }
504
505     /**
506      * Returns the popup menu's label
507      *
508      * @return a string containing the popup menu's label
509      * @see #setLabel
510      */

511     public String JavaDoc getLabel() {
512         return label;
513     }
514     
515     /**
516      * Sets the popup menu's label. Different look and feels may choose
517      * to display or not display this.
518      *
519      * @param label a string specifying the label for the popup menu
520      *
521      * @see #setLabel
522      * @beaninfo
523      * description: The label for the popup menu.
524      * bound: true
525      */

526     public void setLabel(String JavaDoc label) {
527         String JavaDoc oldValue = this.label;
528         this.label = label;
529         firePropertyChange("label", oldValue, label);
530         if (accessibleContext != null) {
531             accessibleContext.firePropertyChange(
532                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
533                 oldValue, label);
534         }
535         invalidate();
536         repaint();
537     }
538
539     /**
540      * Appends a new separator at the end of the menu.
541      */

542     public void addSeparator() {
543         add( new JPopupMenu.Separator JavaDoc() );
544     }
545
546     /**
547      * Inserts a menu item for the specified <code>Action</code> object at
548      * a given position.
549      *
550      * @param a the <code>Action</code> object to insert
551      * @param index specifies the position at which to insert the
552      * <code>Action</code>, where 0 is the first
553      * @exception IllegalArgumentException if <code>index</code> < 0
554      * @see Action
555      */

556     public void insert(Action JavaDoc a, int index) {
557     JMenuItem JavaDoc mi = createActionComponent(a);
558     mi.setAction(a);
559         insert(mi, index);
560     }
561
562     /**
563      * Inserts the specified component into the menu at a given
564      * position.
565      *
566      * @param component the <code>Component</code> to insert
567      * @param index specifies the position at which
568      * to insert the component, where 0 is the first
569      * @exception IllegalArgumentException if <code>index</code> < 0
570      */

571     public void insert(Component component, int index) {
572         if (index < 0) {
573             throw new IllegalArgumentException JavaDoc("index less than zero.");
574         }
575
576         int nitems = getComponentCount();
577     // PENDING(ges): Why not use an array?
578
Vector JavaDoc tempItems = new Vector JavaDoc();
579
580         /* Remove the item at index, nitems-index times
581            storing them in a temporary vector in the
582            order they appear on the menu.
583            */

584         for (int i = index ; i < nitems; i++) {
585             tempItems.addElement(getComponent(index));
586             remove(index);
587         }
588
589         add(component);
590
591         /* Add the removed items back to the menu, they are
592            already in the correct order in the temp vector.
593            */

594         for (int i = 0; i < tempItems.size() ; i++) {
595             add((Component)tempItems.elementAt(i));
596         }
597     }
598
599     /**
600      * Adds a <code>PopupMenu</code> listener.
601      *
602      * @param l the <code>PopupMenuListener</code> to add
603      */

604     public void addPopupMenuListener(PopupMenuListener l) {
605         listenerList.add(PopupMenuListener.class,l);
606     }
607
608     /**
609      * Removes a <code>PopupMenu</code> listener.
610      *
611      * @param l the <code>PopupMenuListener</code> to remove
612      */

613     public void removePopupMenuListener(PopupMenuListener l) {
614         listenerList.remove(PopupMenuListener.class,l);
615     }
616
617     /**
618      * Returns an array of all the <code>PopupMenuListener</code>s added
619      * to this JMenuItem with addPopupMenuListener().
620      *
621      * @return all of the <code>PopupMenuListener</code>s added or an empty
622      * array if no listeners have been added
623      * @since 1.4
624      */

625     public PopupMenuListener[] getPopupMenuListeners() {
626         return (PopupMenuListener[])listenerList.getListeners(
627                 PopupMenuListener.class);
628     }
629
630     /**
631      * Adds a <code>MenuKeyListener</code> to the popup menu.
632      *
633      * @param l the <code>MenuKeyListener</code> to be added
634      * @since 1.5
635      */

636     public void addMenuKeyListener(MenuKeyListener l) {
637         listenerList.add(MenuKeyListener.class, l);
638     }
639
640     /**
641      * Removes a <code>MenuKeyListener</code> from the popup menu.
642      *
643      * @param l the <code>MenuKeyListener</code> to be removed
644      * @since 1.5
645      */

646     public void removeMenuKeyListener(MenuKeyListener l) {
647         listenerList.remove(MenuKeyListener.class, l);
648     }
649
650     /**
651      * Returns an array of all the <code>MenuKeyListener</code>s added
652      * to this JPopupMenu with addMenuKeyListener().
653      *
654      * @return all of the <code>MenuKeyListener</code>s added or an empty
655      * array if no listeners have been added
656      * @since 1.5
657      */

658     public MenuKeyListener[] getMenuKeyListeners() {
659         return (MenuKeyListener[])listenerList.getListeners(
660                 MenuKeyListener.class);
661     }
662
663     /**
664      * Notifies <code>PopupMenuListener</code>s that this popup menu will
665      * become visible.
666      */

667     protected void firePopupMenuWillBecomeVisible() {
668         Object JavaDoc[] listeners = listenerList.getListenerList();
669         PopupMenuEvent e=null;
670         for (int i = listeners.length-2; i>=0; i-=2) {
671             if (listeners[i]==PopupMenuListener.class) {
672                 if (e == null)
673                     e = new PopupMenuEvent(this);
674                 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
675             }
676         }
677     }
678     
679     /**
680      * Notifies <code>PopupMenuListener</code>s that this popup menu will
681      * become invisible.
682      */

683     protected void firePopupMenuWillBecomeInvisible() {
684         Object JavaDoc[] listeners = listenerList.getListenerList();
685         PopupMenuEvent e=null;
686         for (int i = listeners.length-2; i>=0; i-=2) {
687             if (listeners[i]==PopupMenuListener.class) {
688                 if (e == null)
689                     e = new PopupMenuEvent(this);
690                 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
691             }
692         }
693     }
694     
695     /**
696      * Notifies <code>PopupMenuListeners</code> that this popup menu is
697      * cancelled.
698      */

699     protected void firePopupMenuCanceled() {
700         Object JavaDoc[] listeners = listenerList.getListenerList();
701         PopupMenuEvent e=null;
702         for (int i = listeners.length-2; i>=0; i-=2) {
703             if (listeners[i]==PopupMenuListener.class) {
704                 if (e == null)
705                     e = new PopupMenuEvent(this);
706                 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
707             }
708         }
709     }
710
711     /**
712      * Always returns true since popups, by definition, should always
713      * be on top of all other windows.
714      * @return true
715      */

716     // package private
717
boolean alwaysOnTop() {
718     return true;
719     }
720
721     /**
722      * Lays out the container so that it uses the minimum space
723      * needed to display its contents.
724      */

725     public void pack() {
726         if(popup != null) {
727             Dimension pref = getPreferredSize();
728
729             if (pref == null || pref.width != getWidth() ||
730                                 pref.height != getHeight()) {
731                 popup = getPopup();
732             } else {
733                 validate();
734             }
735         }
736     }
737
738     /**
739      * Sets the visibility of the popup menu.
740      *
741      * @param b true to make the popup visible, or false to
742      * hide it
743      * @beaninfo
744      * bound: true
745      * description: Makes the popup visible
746      */

747     public void setVisible(boolean b) {
748     if (DEBUG) {
749         System.out.println("JPopupMenu.setVisible " + b);
750     }
751
752         // Is it a no-op?
753
if (b == isVisible())
754             return;
755
756         // if closing, first close all Submenus
757
if (b == false) {
758
759         // 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
760
// a protected method and cannot be called from BasicPopupMenuUI directly
761
// The real solution could be to make
762
// firePopupMenuCanceled public and call it directly.
763
Boolean JavaDoc doCanceled = (Boolean JavaDoc)getClientProperty("JPopupMenu.firePopupMenuCanceled");
764         if (doCanceled != null && doCanceled == Boolean.TRUE) {
765         putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
766         firePopupMenuCanceled();
767         }
768             getSelectionModel().clearSelection();
769         
770         } else {
771             // This is a popup menu with MenuElement children,
772
// set selection path before popping up!
773
if (isPopupMenu()) {
774         if (getSubElements().length > 0) {
775             MenuElement JavaDoc me[] = new MenuElement JavaDoc[2];
776             me[0]=(MenuElement JavaDoc)this;
777             me[1]=getSubElements()[0];
778             MenuSelectionManager.defaultManager().setSelectedPath(me);
779         } else {
780             MenuElement JavaDoc me[] = new MenuElement JavaDoc[1];
781             me[0]=(MenuElement JavaDoc)this;
782             MenuSelectionManager.defaultManager().setSelectedPath(me);
783         }
784         }
785         }
786
787         if(b) {
788             firePopupMenuWillBecomeVisible();
789             popup = getPopup();
790         firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
791
792        
793     } else if(popup != null) {
794             firePopupMenuWillBecomeInvisible();
795             popup.hide();
796             popup = null;
797         firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
798             // 4694797: When popup menu is made invisible, selected path
799
// should be cleared
800
if (isPopupMenu()) {
801                 MenuSelectionManager.defaultManager().clearSelectedPath();
802             }
803         }
804     }
805
806     /**
807      * Returns a <code>Popup</code> instance from the
808      * <code>PopupMenuUI</code> that has had <code>show</code> invoked on
809      * it. If the current <code>popup</code> is non-null,
810      * this will invoke <code>dispose</code> of it, and then
811      * <code>show</code> the new one.
812      * <p>
813      * This does NOT fire any events, it is up the caller to dispatch
814      * the necessary events.
815      */

816     private Popup JavaDoc getPopup() {
817         Popup JavaDoc oldPopup = popup;
818
819         if (oldPopup != null) {
820             oldPopup.hide();
821         }
822         PopupFactory JavaDoc popupFactory = PopupFactory.getSharedInstance();
823
824         if (isLightWeightPopupEnabled()) {
825             popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
826         }
827         else {
828             popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
829         }
830
831         // adjust the location of the popup
832
Point p = adjustPopupLocationToFitScreen(desiredLocationX,desiredLocationY);
833     desiredLocationX = p.x;
834     desiredLocationY = p.y;
835
836         Popup JavaDoc newPopup = getUI().getPopup(this, desiredLocationX,
837                                           desiredLocationY);
838
839         popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
840         newPopup.show();
841         return newPopup;
842     }
843
844     /**
845      * Returns true if the popup menu is visible (currently
846      * being displayed).
847      */

848     public boolean isVisible() {
849     if(popup != null)
850         return true;
851     else
852         return false;
853     }
854
855     /**
856      * Sets the location of the upper left corner of the
857      * popup menu using x, y coordinates.
858      *
859      * @param x the x coordinate of the popup's new position
860      * in the screen's coordinate space
861      * @param y the y coordinate of the popup's new position
862      * in the screen's coordinate space
863      * @beaninfo
864      * description: The location of the popup menu.
865      */

866     public void setLocation(int x, int y) {
867         int oldX = desiredLocationX;
868         int oldY = desiredLocationY;
869
870         desiredLocationX = x;
871         desiredLocationY = y;
872         if(popup != null && (x != oldX || y != oldY)) {
873             popup = getPopup();
874         }
875     }
876
877     /**
878      * Returns true if the popup menu is a standalone popup menu
879      * rather than the submenu of a <code>JMenu</code>.
880      *
881      * @return true if this menu is a standalone popup menu, otherwise false
882      */

883     private boolean isPopupMenu() {
884         return ((invoker != null) && !(invoker instanceof JMenu JavaDoc));
885     }
886
887     /**
888      * Returns the component which is the 'invoker' of this
889      * popup menu.
890      *
891      * @return the <code>Component</code> in which the popup menu is displayed
892      */

893     public Component getInvoker() {
894         return this.invoker;
895     }
896
897     /**
898      * Sets the invoker of this popup menu -- the component in which
899      * the popup menu menu is to be displayed.
900      *
901      * @param invoker the <code>Component</code> in which the popup
902      * menu is displayed
903      * @beaninfo
904      * description: The invoking component for the popup menu
905      * expert: true
906      */

907     public void setInvoker(Component invoker) {
908         Component oldInvoker = this.invoker;
909         this.invoker = invoker;
910         if ((oldInvoker != this.invoker) && (ui != null)) {
911             ui.uninstallUI(this);
912             ui.installUI(this);
913         }
914         invalidate();
915     }
916
917     /**
918      * Displays the popup menu at the position x,y in the coordinate
919      * space of the component invoker.
920      *
921      * @param invoker the component in whose space the popup menu is to appear
922      * @param x the x coordinate in invoker's coordinate space at which
923      * the popup menu is to be displayed
924      * @param y the y coordinate in invoker's coordinate space at which
925      * the popup menu is to be displayed
926      */

927     public void show(Component invoker, int x, int y) {
928     if (DEBUG) {
929         System.out.println("in JPopupMenu.show " );
930     }
931         setInvoker(invoker);
932         Frame newFrame = getFrame(invoker);
933         if (newFrame != frame) {
934             // Use the invoker's frame so that events
935
// are propagated properly
936
if (newFrame!=null) {
937                 this.frame = newFrame;
938                 if(popup != null) {
939                     setVisible(false);
940                 }
941             }
942         }
943