/*
* Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.beans.*;
import java.util.Locale;
import java.util.Vector;
import java.util.Hashtable;
import javax.accessibility.*;
import javax.swing.plaf.PopupMenuUI;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.event.*;
import sun.security.util.SecurityConstants;
import java.applet.Applet;
/**
* An implementation of a popup menu -- a small window that pops up
* and displays a series of choices. A JPopupMenu
is used for the
* menu that appears when the user selects an item on the menu bar.
* It is also used for "pull-right" menu that appears when the
* selects a menu item that activates it. Finally, a JPopupMenu
* can also be used anywhere else you want a menu to appear. For
* example, when the user right-clicks in a specified area.
*
* For information and examples of using popup menus, see * How to Use Menus * in The Java Tutorial. *
* Warning: Swing is not thread safe. For more * information see Swing's Threading * Policy. *
* Warning:
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeansTM
* has been added to the java.beans
package.
* Please see {@link java.beans.XMLEncoder}.
*
* @beaninfo
* attribute: isContainer false
* description: A small window that pops up and displays a series of choices.
*
* @author Georges Saab
* @author David Karlton
* @author Arnaud Weber
*/
@SuppressWarnings("serial")
public class JPopupMenu extends JComponent implements Accessible,MenuElement {
/**
* @see #getUIClassID
* @see #readObject
*/
private static final String uiClassID = "PopupMenuUI";
/**
* Key used in AppContext to determine if light way popups are the default.
*/
private static final Object defaultLWPopupEnabledKey =
new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
/** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
static boolean popupPostionFixDisabled = false;
static {
popupPostionFixDisabled = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"javax.swing.adjustPopupLocationToFit","")).equals("false");
}
transient Component invoker;
transient Popup popup;
transient Frame frame;
private int desiredLocationX,desiredLocationY;
private String label = null;
private boolean paintBorder = true;
private Insets margin = null;
/**
* Used to indicate if lightweight popups should be used.
*/
private boolean lightWeightPopup = true;
/*
* Model for the selected subcontrol.
*/
private SingleSelectionModel selectionModel;
/* Lock object used in place of class object for synchronization.
* (4187686)
*/
private static final Object classLock = new Object();
/* diagnostic aids -- should be false for production builds. */
private static final boolean TRACE = false; // trace creates and disposes
private static final boolean VERBOSE = false; // show reuse hits/misses
private static final boolean DEBUG = false; // show bad params, misc.
/**
* Sets the default value of the lightWeightPopupEnabled
* property.
*
* @param aFlag true
if popups can be lightweight,
* otherwise false
* @see #getDefaultLightWeightPopupEnabled
* @see #setLightWeightPopupEnabled
*/
public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
Boolean.valueOf(aFlag));
}
/**
* Gets the defaultLightWeightPopupEnabled
property,
* which by default is true
.
*
* @return the value of the defaultLightWeightPopupEnabled
* property
*
* @see #setDefaultLightWeightPopupEnabled
*/
public static boolean getDefaultLightWeightPopupEnabled() {
Boolean b = (Boolean)
SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
if (b == null) {
SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
Boolean.TRUE);
return true;
}
return b.booleanValue();
}
/**
* Constructs a JPopupMenu
without an "invoker".
*/
public JPopupMenu() {
this(null);
}
/**
* Constructs a JPopupMenu
with the specified title.
*
* @param label the string that a UI may use to display as a title
* for the popup menu.
*/
public JPopupMenu(String label) {
this.label = label;
lightWeightPopup = getDefaultLightWeightPopupEnabled();
setSelectionModel(new DefaultSingleSelectionModel());
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
setFocusTraversalKeysEnabled(false);
updateUI();
}
/**
* Returns the look and feel (L&F) object that renders this component.
*
* @return the PopupMenuUI
object that renders this component
*/
public PopupMenuUI getUI() {
return (PopupMenuUI)ui;
}
/**
* Sets the L&F object that renders this component.
*
* @param ui the new PopupMenuUI
L&F object
* @see UIDefaults#getUI
* @beaninfo
* bound: true
* hidden: true
* attribute: visualUpdate true
* description: The UI object that implements the Component's LookAndFeel.
*/
public void setUI(PopupMenuUI ui) {
super.setUI(ui);
}
/**
* Resets the UI property to a value from the current look and feel.
*
* @see JComponent#updateUI
*/
public void updateUI() {
setUI((PopupMenuUI)UIManager.getUI(this));
}
/**
* Returns the name of the L&F class that renders this component.
*
* @return the string "PopupMenuUI"
* @see JComponent#getUIClassID
* @see UIDefaults#getUI
*/
public String getUIClassID() {
return uiClassID;
}
protected void processFocusEvent(FocusEvent evt) {
super.processFocusEvent(evt);
}
/**
* Processes key stroke events such as mnemonics and accelerators.
*
* @param evt the key event to be processed
*/
protected void processKeyEvent(KeyEvent evt) {
MenuSelectionManager.defaultManager().processKeyEvent(evt);
if (evt.isConsumed()) {
return;
}
super.processKeyEvent(evt);
}
/**
* Returns the model object that handles single selections.
*
* @return the selectionModel
property
* @see SingleSelectionModel
*/
public SingleSelectionModel getSelectionModel() {
return selectionModel;
}
/**
* Sets the model object to handle single selections.
*
* @param model the new SingleSelectionModel
* @see SingleSelectionModel
* @beaninfo
* description: The selection model for the popup menu
* expert: true
*/
public void setSelectionModel(SingleSelectionModel model) {
selectionModel = model;
}
/**
* Appends the specified menu item to the end of this menu.
*
* @param menuItem the JMenuItem
to add
* @return the JMenuItem
added
*/
public JMenuItem add(JMenuItem menuItem) {
super.add(menuItem);
return menuItem;
}
/**
* Creates a new menu item with the specified text and appends
* it to the end of this menu.
*
* @param s the string for the menu item to be added
*/
public JMenuItem add(String s) {
return add(new JMenuItem(s));
}
/**
* Appends a new menu item to the end of the menu which
* dispatches the specified Action
object.
*
* @param a the Action
to add to the menu
* @return the new menu item
* @see Action
*/
public JMenuItem add(Action a) {
JMenuItem mi = createActionComponent(a);
mi.setAction(a);
add(mi);
return mi;
}
/**
* Returns an point which has been adjusted to take into account of the
* desktop bounds, taskbar and multi-monitor configuration.
*
* This adustment may be cancelled by invoking the application with
* -Djavax.swing.adjustPopupLocationToFit=false
*/
Point adjustPopupLocationToFitScreen(int xPosition, int yPosition) {
Point popupLocation = new Point(xPosition, yPosition);
if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless()) {
return popupLocation;
}
// Get screen bounds
Rectangle scrBounds;
GraphicsConfiguration gc = getCurrentGraphicsConfiguration(popupLocation);
Toolkit toolkit = Toolkit.getDefaultToolkit();
if(gc != null) {
// If we have GraphicsConfiguration use it to get screen bounds
scrBounds = gc.getBounds();
} else {
// If we don't have GraphicsConfiguration use primary screen
scrBounds = new Rectangle(toolkit.getScreenSize());
}
// Calculate the screen size that popup should fit
Dimension popupSize = JPopupMenu.this.getPreferredSize();
long popupRightX = (long)popupLocation.x + (long)popupSize.width;
long popupBottomY = (long)popupLocation.y + (long)popupSize.height;
int scrWidth = scrBounds.width;
int scrHeight = scrBounds.height;
if (!canPopupOverlapTaskBar()) {
// Insets include the task bar. Take them into account.
Insets scrInsets = toolkit.getScreenInsets(gc);
scrBounds.x += scrInsets.left;
scrBounds.y += scrInsets.top;
scrWidth -= scrInsets.left + scrInsets.right;
scrHeight -= scrInsets.top + scrInsets.bottom;
}
int scrRightX = scrBounds.x + scrWidth;
int scrBottomY = scrBounds.y + scrHeight;
// Ensure that popup menu fits the screen
if (popupRightX > (long) scrRightX) {
popupLocation.x = scrRightX - popupSize.width;
}
if (popupBottomY > (long) scrBottomY) {
popupLocation.y = scrBottomY - popupSize.height;
}
if (popupLocation.x < scrBounds.x) {
popupLocation.x = scrBounds.x;
}
if (popupLocation.y < scrBounds.y) {
popupLocation.y = scrBounds.y;
}
return popupLocation;
}
/**
* Tries to find GraphicsConfiguration
* that contains the mouse cursor position.
* Can return null.
*/
private GraphicsConfiguration getCurrentGraphicsConfiguration(
Point popupLocation) {
GraphicsConfiguration gc = null;
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gd = ge.getScreenDevices();
for(int i = 0; i < gd.length; i++) {
if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
GraphicsConfiguration dgc =
gd[i].getDefaultConfiguration();
if(dgc.getBounds().contains(popupLocation)) {
gc = dgc;
break;
}
}
}
// If not found and we have invoker, ask invoker about his gc
if(gc == null && getInvoker() != null) {
gc = getInvoker().getGraphicsConfiguration();
}
return gc;
}
/**
* Checks that there are enough security permissions
* to make popup "always on top", which allows to show it above the task bar.
*/
static boolean canPopupOverlapTaskBar() {
boolean result = true;
try {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(
SecurityConstants.AWT.SET_WINDOW_ALWAYS_ON_TOP_PERMISSION);
}
} catch (SecurityException se) {
// There is no permission to show popups over the task bar
result = false;
}
return result;
}
/**
* Factory method which creates the
* This does NOT fire any events, it is up the caller to dispatch
* the necessary events.
*/
private Popup getPopup() {
Popup oldPopup = popup;
if (oldPopup != null) {
oldPopup.hide();
}
PopupFactory popupFactory = PopupFactory.getSharedInstance();
if (isLightWeightPopupEnabled()) {
popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
}
else {
popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
}
// adjust the location of the popup
Point p = adjustPopupLocationToFitScreen(desiredLocationX,desiredLocationY);
desiredLocationX = p.x;
desiredLocationY = p.y;
Popup newPopup = getUI().getPopup(this, desiredLocationX,
desiredLocationY);
popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
newPopup.show();
return newPopup;
}
/**
* Returns true if the popup menu is visible (currently
* being displayed).
*/
public boolean isVisible() {
return popup != null;
}
/**
* Sets the location of the upper left corner of the
* popup menu using x, y coordinates.
*
* @param x the x coordinate of the popup's new position
* in the screen's coordinate space
* @param y the y coordinate of the popup's new position
* in the screen's coordinate space
* @beaninfo
* description: The location of the popup menu.
*/
public void setLocation(int x, int y) {
int oldX = desiredLocationX;
int oldY = desiredLocationY;
desiredLocationX = x;
desiredLocationY = y;
if(popup != null && (x != oldX || y != oldY)) {
popup = getPopup();
}
}
/**
* Returns true if the popup menu is a standalone popup menu
* rather than the submenu of a JMenuItem
for
* Actions
added to the JPopupMenu
.
*
* @param a the Action
for the menu item to be added
* @return the new menu item
* @see Action
*
* @since 1.3
*/
protected JMenuItem createActionComponent(Action a) {
JMenuItem mi = new JMenuItem() {
protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
PropertyChangeListener pcl = createActionChangeListener(this);
if (pcl == null) {
pcl = super.createActionPropertyChangeListener(a);
}
return pcl;
}
};
mi.setHorizontalTextPosition(JButton.TRAILING);
mi.setVerticalTextPosition(JButton.CENTER);
return mi;
}
/**
* Returns a properly configured PropertyChangeListener
* which updates the control as changes to the Action
occur.
*/
protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
return b.createActionPropertyChangeListener0(b.getAction());
}
/**
* Removes the component at the specified index from this popup menu.
*
* @param pos the position of the item to be removed
* @exception IllegalArgumentException if the value of
* pos
< 0, or if the value of
* pos
is greater than the
* number of items
*/
public void remove(int pos) {
if (pos < 0) {
throw new IllegalArgumentException("index less than zero.");
}
if (pos > getComponentCount() -1) {
throw new IllegalArgumentException("index greater than the number of items.");
}
super.remove(pos);
}
/**
* Sets the value of the lightWeightPopupEnabled
property,
* which by default is true
.
* By default, when a look and feel displays a popup,
* it can choose to
* use a lightweight (all-Java) popup.
* Lightweight popup windows are more efficient than heavyweight
* (native peer) windows,
* but lightweight and heavyweight components do not mix well in a GUI.
* If your application mixes lightweight and heavyweight components,
* you should disable lightweight popups.
* Some look and feels might always use heavyweight popups,
* no matter what the value of this property.
*
* @param aFlag false
to disable lightweight popups
* @beaninfo
* description: Determines whether lightweight popups are used when possible
* expert: true
*
* @see #isLightWeightPopupEnabled
*/
public void setLightWeightPopupEnabled(boolean aFlag) {
// NOTE: this use to set the flag on a shared JPopupMenu, which meant
// this effected ALL JPopupMenus.
lightWeightPopup = aFlag;
}
/**
* Gets the lightWeightPopupEnabled
property.
*
* @return the value of the lightWeightPopupEnabled
property
* @see #setLightWeightPopupEnabled
*/
public boolean isLightWeightPopupEnabled() {
return lightWeightPopup;
}
/**
* Returns the popup menu's label
*
* @return a string containing the popup menu's label
* @see #setLabel
*/
public String getLabel() {
return label;
}
/**
* Sets the popup menu's label. Different look and feels may choose
* to display or not display this.
*
* @param label a string specifying the label for the popup menu
*
* @see #setLabel
* @beaninfo
* description: The label for the popup menu.
* bound: true
*/
public void setLabel(String label) {
String oldValue = this.label;
this.label = label;
firePropertyChange("label", oldValue, label);
if (accessibleContext != null) {
accessibleContext.firePropertyChange(
AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
oldValue, label);
}
invalidate();
repaint();
}
/**
* Appends a new separator at the end of the menu.
*/
public void addSeparator() {
add( new JPopupMenu.Separator() );
}
/**
* Inserts a menu item for the specified Action
object at
* a given position.
*
* @param a the Action
object to insert
* @param index specifies the position at which to insert the
* Action
, where 0 is the first
* @exception IllegalArgumentException if index
< 0
* @see Action
*/
public void insert(Action a, int index) {
JMenuItem mi = createActionComponent(a);
mi.setAction(a);
insert(mi, index);
}
/**
* Inserts the specified component into the menu at a given
* position.
*
* @param component the Component
to insert
* @param index specifies the position at which
* to insert the component, where 0 is the first
* @exception IllegalArgumentException if index
< 0
*/
public void insert(Component component, int index) {
if (index < 0) {
throw new IllegalArgumentException("index less than zero.");
}
int nitems = getComponentCount();
// PENDING(ges): Why not use an array?
VectorPopupMenu
listener.
*
* @param l the PopupMenuListener
to add
*/
public void addPopupMenuListener(PopupMenuListener l) {
listenerList.add(PopupMenuListener.class,l);
}
/**
* Removes a PopupMenu
listener.
*
* @param l the PopupMenuListener
to remove
*/
public void removePopupMenuListener(PopupMenuListener l) {
listenerList.remove(PopupMenuListener.class,l);
}
/**
* Returns an array of all the PopupMenuListener
s added
* to this JMenuItem with addPopupMenuListener().
*
* @return all of the PopupMenuListener
s added or an empty
* array if no listeners have been added
* @since 1.4
*/
public PopupMenuListener[] getPopupMenuListeners() {
return listenerList.getListeners(PopupMenuListener.class);
}
/**
* Adds a MenuKeyListener
to the popup menu.
*
* @param l the MenuKeyListener
to be added
* @since 1.5
*/
public void addMenuKeyListener(MenuKeyListener l) {
listenerList.add(MenuKeyListener.class, l);
}
/**
* Removes a MenuKeyListener
from the popup menu.
*
* @param l the MenuKeyListener
to be removed
* @since 1.5
*/
public void removeMenuKeyListener(MenuKeyListener l) {
listenerList.remove(MenuKeyListener.class, l);
}
/**
* Returns an array of all the MenuKeyListener
s added
* to this JPopupMenu with addMenuKeyListener().
*
* @return all of the MenuKeyListener
s added or an empty
* array if no listeners have been added
* @since 1.5
*/
public MenuKeyListener[] getMenuKeyListeners() {
return listenerList.getListeners(MenuKeyListener.class);
}
/**
* Notifies PopupMenuListener
s that this popup menu will
* become visible.
*/
protected void firePopupMenuWillBecomeVisible() {
Object[] listeners = listenerList.getListenerList();
PopupMenuEvent e=null;
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==PopupMenuListener.class) {
if (e == null)
e = new PopupMenuEvent(this);
((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
}
}
}
/**
* Notifies PopupMenuListener
s that this popup menu will
* become invisible.
*/
protected void firePopupMenuWillBecomeInvisible() {
Object[] listeners = listenerList.getListenerList();
PopupMenuEvent e=null;
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==PopupMenuListener.class) {
if (e == null)
e = new PopupMenuEvent(this);
((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
}
}
}
/**
* Notifies PopupMenuListeners
that this popup menu is
* cancelled.
*/
protected void firePopupMenuCanceled() {
Object[] listeners = listenerList.getListenerList();
PopupMenuEvent e=null;
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==PopupMenuListener.class) {
if (e == null)
e = new PopupMenuEvent(this);
((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
}
}
}
/**
* Always returns true since popups, by definition, should always
* be on top of all other windows.
* @return true
*/
// package private
boolean alwaysOnTop() {
return true;
}
/**
* Lays out the container so that it uses the minimum space
* needed to display its contents.
*/
public void pack() {
if(popup != null) {
Dimension pref = getPreferredSize();
if (pref == null || pref.width != getWidth() ||
pref.height != getHeight()) {
popup = getPopup();
} else {
validate();
}
}
}
/**
* Sets the visibility of the popup menu.
*
* @param b true to make the popup visible, or false to
* hide it
* @beaninfo
* bound: true
* description: Makes the popup visible
*/
public void setVisible(boolean b) {
if (DEBUG) {
System.out.println("JPopupMenu.setVisible " + b);
}
// Is it a no-op?
if (b == isVisible())
return;
// if closing, first close all Submenus
if (b == false) {
// 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
// a protected method and cannot be called from BasicPopupMenuUI directly
// The real solution could be to make
// firePopupMenuCanceled public and call it directly.
Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled");
if (doCanceled != null && doCanceled == Boolean.TRUE) {
putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
firePopupMenuCanceled();
}
getSelectionModel().clearSelection();
} else {
// This is a popup menu with MenuElement children,
// set selection path before popping up!
if (isPopupMenu()) {
MenuElement me[] = new MenuElement[1];
me[0] = this;
MenuSelectionManager.defaultManager().setSelectedPath(me);
}
}
if(b) {
firePopupMenuWillBecomeVisible();
popup = getPopup();
firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
} else if(popup != null) {
firePopupMenuWillBecomeInvisible();
popup.hide();
popup = null;
firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
// 4694797: When popup menu is made invisible, selected path
// should be cleared
if (isPopupMenu()) {
MenuSelectionManager.defaultManager().clearSelectedPath();
}
}
}
/**
* Returns a Popup
instance from the
* PopupMenuUI
that has had show
invoked on
* it. If the current popup
is non-null,
* this will invoke dispose
of it, and then
* show
the new one.
* JMenu
.
*
* @return true if this menu is a standalone popup menu, otherwise false
*/
private boolean isPopupMenu() {
return ((invoker != null) && !(invoker instanceof JMenu));
}
/**
* Returns the component which is the 'invoker' of this
* popup menu.
*
* @return the Component
in which the popup menu is displayed
*/
public Component getInvoker() {
return this.invoker;
}
/**
* Sets the invoker of this popup menu -- the component in which
* the popup menu menu is to be displayed.
*
* @param invoker the Component
in which the popup
* menu is displayed
* @beaninfo
* description: The invoking component for the popup menu
* expert: true
*/
public void setInvoker(Component invoker) {
Component oldInvoker = this.invoker;
this.invoker = invoker;
if ((oldInvoker != this.invoker) && (ui != null)) {
ui.uninstallUI(this);
ui.installUI(this);
}
invalidate();
}
/**
* Displays the popup menu at the position x,y in the coordinate
* space of the component invoker.
*
* @param invoker the component in whose space the popup menu is to appear
* @param x the x coordinate in invoker's coordinate space at which
* the popup menu is to be displayed
* @param y the y coordinate in invoker's coordinate space at which
* the popup menu is to be displayed
*/
public void show(Component invoker, int x, int y) {
if (DEBUG) {
System.out.println("in JPopupMenu.show " );
}
setInvoker(invoker);
Frame newFrame = getFrame(invoker);
if (newFrame != frame) {
// Use the invoker's frame so that events
// are propagated properly
if (newFrame!=null) {
this.frame = newFrame;
if(popup != null) {
setVisible(false);
}
}
}
Point invokerOrigin;
if (invoker != null) {
invokerOrigin = invoker.getLocationOnScreen();
// To avoid integer overflow
long lx, ly;
lx = ((long) invokerOrigin.x) +
((long) x);
ly = ((long) invokerOrigin.y) +
((long) y);
if(lx > Integer.MAX_VALUE) lx = Integer.MAX_VALUE;
if(lx < Integer.MIN_VALUE) lx = Integer.MIN_VALUE;
if(ly > Integer.MAX_VALUE) ly = Integer.MAX_VALUE;
if(ly < Integer.MIN_VALUE) ly = Integer.MIN_VALUE;
setLocation((int) lx, (int) ly);
} else {
setLocation(x, y);
}
setVisible(true);
}
/**
* Returns the popup menu which is at the root of the menu system
* for this popup menu.
*
* @return the topmost grandparent JPopupMenu
*/
JPopupMenu getRootPopupMenu() {
JPopupMenu mp = this;
while((mp!=null) && (mp.isPopupMenu()!=true) &&
(mp.getInvoker() != null) &&
(mp.getInvoker().getParent() != null) &&
(mp.getInvoker().getParent() instanceof JPopupMenu)
) {
mp = (JPopupMenu) mp.getInvoker().getParent();
}
return mp;
}
/**
* Returns the component at the specified index.
*
* @param i the index of the component, where 0 is the first
* @return the Component
at that index
* @deprecated replaced by {@link java.awt.Container#getComponent(int)}
*/
@Deprecated
public Component getComponentAtIndex(int i) {
return getComponent(i);
}
/**
* Returns the index of the specified component.
*
* @param c the Component
to find
* @return the index of the component, where 0 is the first;
* or -1 if the component is not found
*/
public int getComponentIndex(Component c) {
int ncomponents = this.getComponentCount();
Component[] component = this.getComponents();
for (int i = 0 ; i < ncomponents ; i++) {
Component comp = component[i];
if (comp == c)
return i;
}
return -1;
}
/**
* Sets the size of the Popup window using a Dimension
object.
* This is equivalent to setPreferredSize(d)
.
*
* @param d the Dimension
specifying the new size
* of this component.
* @beaninfo
* description: The size of the popup menu
*/
public void setPopupSize(Dimension d) {
Dimension oldSize = getPreferredSize();
setPreferredSize(d);
if (popup != null) {
Dimension newSize = getPreferredSize();
if (!oldSize.equals(newSize)) {
popup = getPopup();
}
}
}
/**
* Sets the size of the Popup window to the specified width and
* height. This is equivalent to
* setPreferredSize(new Dimension(width, height))
.
*
* @param width the new width of the Popup in pixels
* @param height the new height of the Popup in pixels
* @beaninfo
* description: The size of the popup menu
*/
public void setPopupSize(int width, int height) {
setPopupSize(new Dimension(width, height));
}
/**
* Sets the currently selected component, This will result
* in a change to the selection model.
*
* @param sel the Component
to select
* @beaninfo
* description: The selected component on the popup menu
* expert: true
* hidden: true
*/
public void setSelected(Component sel) {
SingleSelectionModel model = getSelectionModel();
int index = getComponentIndex(sel);
model.setSelectedIndex(index);
}
/**
* Checks whether the border should be painted.
*
* @return true if the border is painted, false otherwise
* @see #setBorderPainted
*/
public boolean isBorderPainted() {
return paintBorder;
}
/**
* Sets whether the border should be painted.
*
* @param b if true, the border is painted.
* @see #isBorderPainted
* @beaninfo
* description: Is the border of the popup menu painted
*/
public void setBorderPainted(boolean b) {
paintBorder = b;
repaint();
}
/**
* Paints the popup menu's border if the borderPainted
* property is true
.
* @param g the Graphics
object
*
* @see JComponent#paint
* @see JComponent#setBorder
*/
protected void paintBorder(Graphics g) {
if (isBorderPainted()) {
super.paintBorder(g);
}
}
/**
* Returns the margin, in pixels, between the popup menu's border and
* its containees.
*
* @return an Insets
object containing the margin values.
*/
public Insets getMargin() {
if(margin == null) {
return new Insets(0,0,0,0);
} else {
return margin;
}
}
/**
* Examines the list of menu items to determine whether
* popup
is a popup menu.
*
* @param popup a JPopupMenu
* @return true if popup
*/
boolean isSubPopupMenu(JPopupMenu popup) {
int ncomponents = this.getComponentCount();
Component[] component = this.getComponents();
for (int i = 0 ; i < ncomponents ; i++) {
Component comp = component[i];
if (comp instanceof JMenu) {
JMenu menu = (JMenu)comp;
JPopupMenu subPopup = menu.getPopupMenu();
if (subPopup == popup)
return true;
if (subPopup.isSubPopupMenu(popup))
return true;
}
}
return false;
}
private static Frame getFrame(Component c) {
Component w = c;
while(!(w instanceof Frame) && (w!=null)) {
w = w.getParent();
}
return (Frame)w;
}
/**
* Returns a string representation of this JPopupMenu
.
* This method
* is intended to be used only for debugging purposes, and the
* content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be null
.
*
* @return a string representation of this JPopupMenu
.
*/
protected String paramString() {
String labelString = (label != null ?
label : "");
String paintBorderString = (paintBorder ?
"true" : "false");
String marginString = (margin != null ?
margin.toString() : "");
String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ?
"true" : "false");
return super.paramString() +
",desiredLocationX=" + desiredLocationX +
",desiredLocationY=" + desiredLocationY +
",label=" + labelString +
",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
",margin=" + marginString +
",paintBorder=" + paintBorderString;
}
/////////////////
// Accessibility support
////////////////
/**
* Gets the AccessibleContext associated with this JPopupMenu.
* For JPopupMenus, the AccessibleContext takes the form of an
* AccessibleJPopupMenu.
* A new AccessibleJPopupMenu instance is created if necessary.
*
* @return an AccessibleJPopupMenu that serves as the
* AccessibleContext of this JPopupMenu
*/
public AccessibleContext getAccessibleContext() {
if (accessibleContext == null) {
accessibleContext = new AccessibleJPopupMenu();
}
return accessibleContext;
}
/**
* This class implements accessibility support for the
* JPopupMenu
class. It provides an implementation of the
* Java Accessibility API appropriate to popup menu user-interface
* elements.
*/
@SuppressWarnings("serial")
protected class AccessibleJPopupMenu extends AccessibleJComponent
implements PropertyChangeListener {
/**
* AccessibleJPopupMenu constructor
*
* @since 1.5
*/
protected AccessibleJPopupMenu() {
JPopupMenu.this.addPropertyChangeListener(this);
}
/**
* Get the role of this object.
*
* @return an instance of AccessibleRole describing the role of
* the object
*/
public AccessibleRole getAccessibleRole() {
return AccessibleRole.POPUP_MENU;
}
/**
* This method gets called when a bound property is changed.
* @param e A PropertyChangeEvent
object describing
* the event source and the property that has changed. Must not be null.
*
* @throws NullPointerException if the parameter is null.
* @since 1.5
*/
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if (propertyName == "visible") {
if (e.getOldValue() == Boolean.FALSE &&
e.getNewValue() == Boolean.TRUE) {
handlePopupIsVisibleEvent(true);
} else if (e.getOldValue() == Boolean.TRUE &&
e.getNewValue() == Boolean.FALSE) {
handlePopupIsVisibleEvent(false);
}
}
}
/*
* Handles popup "visible" PropertyChangeEvent
*/
private void handlePopupIsVisibleEvent(boolean visible) {
if (visible) {
// notify listeners that the popup became visible
firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
null, AccessibleState.VISIBLE);
// notify listeners that a popup list item is selected
fireActiveDescendant();
} else {
// notify listeners that the popup became hidden
firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
AccessibleState.VISIBLE, null);
}
}
/*
* Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners
* on the popup menu invoker that a popup list item has been selected
*/
private void fireActiveDescendant() {
if (JPopupMenu.this instanceof BasicComboPopup) {
// get the popup list
JList> popupList = ((BasicComboPopup)JPopupMenu.this).getList();
if (popupList == null) {
return;
}
// get the first selected item
AccessibleContext ac = popupList.getAccessibleContext();
AccessibleSelection selection = ac.getAccessibleSelection();
if (selection == null) {
return;
}
Accessible a = selection.getAccessibleSelection(0);
if (a == null) {
return;
}
AccessibleContext selectedItem = a.getAccessibleContext();
// fire the event with the popup invoker as the source.
if (selectedItem != null && invoker != null) {
AccessibleContext invokerContext = invoker.getAccessibleContext();
if (invokerContext != null) {
// Check invokerContext because Component.getAccessibleContext
// returns null. Classes that extend Component are responsible
// for returning a non-null AccessibleContext.
invokerContext.firePropertyChange(
ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
null, selectedItem);
}
}
}
}
} // inner class AccessibleJPopupMenu
////////////
// Serialization support.
////////////
private void writeObject(ObjectOutputStream s) throws IOException {
Vector