- /*
- * @(#)JPopupMenu.java 1.191 04/05/18
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- 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 java.applet.Applet;
-
- /**
- * An implementation of a popup menu -- a small window that pops up
- * and displays a series of choices. A <code>JPopupMenu</code> 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 <code>JPopupMenu</code>
- * can also be used anywhere else you want a menu to appear. For
- * example, when the user right-clicks in a specified area.
- * <p>
- * For information and examples of using popup menus, see
- * <a
- href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
- * in <em>The Java Tutorial.</em>
- * <p>
- * <strong>Warning:</strong>
- * 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 JavaBeans<sup><font size="-2">TM</font></sup>
- * has been added to the <code>java.beans</code> package.
- * Please see {@link java.beans.XMLEncoder}.
- *
- * @beaninfo
- * attribute: isContainer false
- * description: A small window that pops up and displays a series of choices.
- *
- * @version 1.191 @(#)JPopupMenu.java 1.191
- * @author Georges Saab
- * @author David Karlton
- * @author Arnaud Weber
- */
- 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 <code>lightWeightPopupEnabled</code>
- * property.
- *
- * @param aFlag <code>true</code> if popups can be lightweight,
- * otherwise <code>false</code>
- * @see #getDefaultLightWeightPopupEnabled
- * @see #setLightWeightPopupEnabled
- */
- public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
- SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
- Boolean.valueOf(aFlag));
- }
-
- /**
- * Gets the <code>defaultLightWeightPopupEnabled</code> property,
- * which by default is <code>true</code>.
- *
- * @return the value of the <code>defaultLightWeightPopupEnabled</code>
- * 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 <code>JPopupMenu</code> without an "invoker".
- */
- public JPopupMenu() {
- this(null);
- }
-
- /**
- * Constructs a <code>JPopupMenu</code> 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 <code>PopupMenuUI</code> object that renders this component
- */
- public PopupMenuUI getUI() {
- return (PopupMenuUI)ui;
- }
-
- /**
- * Sets the L&F object that renders this component.
- *
- * @param ui the new <code>PopupMenuUI</code> 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 <code>selectionModel</code> property
- * @see SingleSelectionModel
- */
- public SingleSelectionModel getSelectionModel() {
- return selectionModel;
- }
-
- /**
- * Sets the model object to handle single selections.
- *
- * @param model the new <code>SingleSelectionModel</code>
- * @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 <code>JMenuItem</code> to add
- * @return the <code>JMenuItem</code> 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 <code>Action</code> object.
- *
- * As of JDK 1.3, this is no longer the preferred method for adding
- * <code>Actions</code> to
- * a container. Instead it is recommended to configure a control with
- * an action using <code>setAction</code>, and then add that control
- * directly to the <code>Container</code>.
- *
- * @param a the <code>Action</code> 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.
- * <p>
- * This adustment may be cancelled by invoking the application with
- * -Djavax.swing.adjustPopupLocationToFit=false
- */
- Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
- Point p = new Point(xposition, yposition);
-
- if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
- return p;
-
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Rectangle screenBounds;
- Insets screenInsets;
- GraphicsConfiguration gc = null;
- // Try to find GraphicsConfiguration, that includes mouse
- // pointer position
- 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(p)) {
- gc = dgc;
- break;
- }
- }
- }
-
- // If not found and we have invoker, ask invoker about his gc
- if(gc == null && getInvoker() != null) {
- gc = getInvoker().getGraphicsConfiguration();
- }
-
- if(gc != null) {
- // If we have GraphicsConfiguration use it to get
- // screen bounds and insets
- screenInsets = toolkit.getScreenInsets(gc);
- screenBounds = gc.getBounds();
- } else {
- // If we don't have GraphicsConfiguration use primary screen
- // and empty insets
- screenInsets = new Insets(0, 0, 0, 0);
- screenBounds = new Rectangle(toolkit.getScreenSize());
- }
-
- int scrWidth = screenBounds.width -
- Math.abs(screenInsets.left+screenInsets.right);
- int scrHeight = screenBounds.height -
- Math.abs(screenInsets.top+screenInsets.bottom);
-
- Dimension size;
-
- size = JPopupMenu.this.getPreferredSize();
-
- if( (p.x + size.width) > screenBounds.x + scrWidth )
- p.x = screenBounds.x + scrWidth - size.width;
-
- if( (p.y + size.height) > screenBounds.y + scrHeight)
- p.y = screenBounds.y + scrHeight - size.height;
-
- /* Change is made to the desired (X,Y) values, when the
- PopupMenu is too tall OR too wide for the screen
- */
- if( p.x < screenBounds.x )
- p.x = screenBounds.x ;
- if( p.y < screenBounds.y )
- p.y = screenBounds.y;
-
- return p;
- }
-
-
- /**
- * Factory method which creates the <code>JMenuItem</code> for
- * <code>Actions</code> added to the <code>JPopupMenu</code>.
- * As of JDK 1.3, this is no
- * longer the preferred method, instead it is recommended to configure
- * a control with an action using <code>setAction</code>,
- * and then adding that
- * control directly to the <code>Container</code>.
- *
- * @param a the <code>Action</code> 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((String)a.getValue(Action.NAME),
- (Icon)a.getValue(Action.SMALL_ICON)){
- 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);
- mi.setEnabled(a.isEnabled());
- return mi;
- }
-
- /**
- * Returns a properly configured <code>PropertyChangeListener</code>
- * which updates the control as changes to the <code>Action</code> occur.
- * As of JDK 1.3, this is no longer the preferred method for adding
- * <code>Actions</code> to
- * a container. Instead it is recommended to configure a control with
- * an action using <code>setAction</code>, and then add that control
- * directly to the <code>Container</code>.
- */
- protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
- return new ActionChangedListener(b);
- }
-
- private class ActionChangedListener implements PropertyChangeListener, Serializable {
- private JMenuItem menuItem;
-
- public ActionChangedListener(JMenuItem mi) {
- super();
- setTarget(mi);
- }
- public void propertyChange(PropertyChangeEvent e) {
- String propertyName = e.getPropertyName();
- if (e.getPropertyName().equals(Action.NAME)) {
- String text = (String) e.getNewValue();
- menuItem.setText(text);
- } else if (propertyName.equals("enabled")) {
- Boolean enabledState = (Boolean) e.getNewValue();
- menuItem.setEnabled(enabledState.booleanValue());
- } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
- Icon icon = (Icon) e.getNewValue();
- menuItem.setIcon(icon);
- menuItem.invalidate();
- menuItem.repaint();
- }
- }
- public void setTarget(JMenuItem b) {
- this.menuItem = b;
- }
- }
-
- /**
- * 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
- * <code>pos</code> < 0, or if the value of
- * <code>pos</code> 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 <code>lightWeightPopupEnabled</code> property,
- * which by default is <code>true</code>.
- * 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 <code>false</code> 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 <code>lightWeightPopupEnabled</code> property.
- *
- * @return the value of the <code>lightWeightPopupEnabled</code> 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 <code>Action</code> object at
- * a given position.
- *
- * @param a the <code>Action</code> object to insert
- * @param index specifies the position at which to insert the
- * <code>Action</code>, where 0 is the first
- * @exception IllegalArgumentException if <code>index</code> < 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 <code>Component</code> to insert
- * @param index specifies the position at which
- * to insert the component, where 0 is the first
- * @exception IllegalArgumentException if <code>index</code> < 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?
- Vector tempItems = new Vector();
-
- /* Remove the item at index, nitems-index times
- storing them in a temporary vector in the
- order they appear on the menu.
- */
- for (int i = index ; i < nitems; i++) {
- tempItems.addElement(getComponent(index));
- remove(index);
- }
-
- add(component);
-
- /* Add the removed items back to the menu, they are
- already in the correct order in the temp vector.
- */
- for (int i = 0; i < tempItems.size() ; i++) {
- add((Component)tempItems.elementAt(i));
- }
- }
-
- /**
- * Adds a <code>PopupMenu</code> listener.
- *
- * @param l the <code>PopupMenuListener</code> to add
- */
- public void addPopupMenuListener(PopupMenuListener l) {
- listenerList.add(PopupMenuListener.class,l);
- }
-
- /**
- * Removes a <code>PopupMenu</code> listener.
- *
- * @param l the <code>PopupMenuListener</code> to remove
- */
- public void removePopupMenuListener(PopupMenuListener l) {
- listenerList.remove(PopupMenuListener.class,l);
- }
-
- /**
- * Returns an array of all the <code>PopupMenuListener</code>s added
- * to this JMenuItem with addPopupMenuListener().
- *
- * @return all of the <code>PopupMenuListener</code>s added or an empty
- * array if no listeners have been added
- * @since 1.4
- */
- public PopupMenuListener[] getPopupMenuListeners() {
- return (PopupMenuListener[])listenerList.getListeners(
- PopupMenuListener.class);
- }
-
- /**
- * Adds a <code>MenuKeyListener</code> to the popup menu.
- *
- * @param l the <code>MenuKeyListener</code> to be added
- * @since 1.5
- */
- public void addMenuKeyListener(MenuKeyListener l) {
- listenerList.add(MenuKeyListener.class, l);
- }
-
- /**
- * Removes a <code>MenuKeyListener</code> from the popup menu.
- *
- * @param l the <code>MenuKeyListener</code> to be removed
- * @since 1.5
- */
- public void removeMenuKeyListener(MenuKeyListener l) {
- listenerList.remove(MenuKeyListener.class, l);
- }
-
- /**
- * Returns an array of all the <code>MenuKeyListener</code>s added
- * to this JPopupMenu with addMenuKeyListener().
- *
- * @return all of the <code>MenuKeyListener</code>s added or an empty
- * array if no listeners have been added
- * @since 1.5
- */
- public MenuKeyListener[] getMenuKeyListeners() {
- return (MenuKeyListener[])listenerList.getListeners(
- MenuKeyListener.class);
- }
-
- /**
- * Notifies <code>PopupMenuListener</code>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 <code>PopupMenuListener</code>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 <code>PopupMenuListeners</code> 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()) {
- if (getSubElements().length > 0) {
- MenuElement me[] = new MenuElement[2];
- me[0]=(MenuElement)this;
- me[1]=getSubElements()[0];
- MenuSelectionManager.defaultManager().setSelectedPath(me);
- } else {
- MenuElement me[] = new MenuElement[1];
- me[0]=(MenuElement)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 <code>Popup</code> instance from the
- * <code>PopupMenuUI</code> that has had <code>show</code> invoked on
- * it. If the current <code>popup</code> is non-null,
- * this will invoke <code>dispose</code> of it, and then
- * <code>show</code> the new one.
- * <p>
- * 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() {
- if(popup != null)
- return true;
- else
- return false;
- }
-
- /**
- * 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 <code>JMenu</code>.
- *
- * @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 <code>Component</code> 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 <code>Component</code> 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();
- setLocation(invokerOrigin.x + x,
- invokerOrigin.y + y);
- } 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 <code>JPopupMenu</code>
- */
- 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 <code>Component</code> at that index
- * @deprecated replaced by <code>getComponent(int i)</code>
- */
- @Deprecated
- public Component getComponentAtIndex(int i) {
- return getComponent(i);
- }
-
- /**
- * Returns the index of the specified component.
- *
- * @param c the <code>Component</code> 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 <code>Dimension</code> object.
- * This is equivalent to <code>setPreferredSize(d)</code>.
- *
- * @param d the <code>Dimension</code> 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
- * <code>setPreferredSize(new Dimension(width, height))</code>.
- *
- * @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 <code>Component</code> 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 <code>borderPainted</code>
- * property is <code>true</code>.
- * @param g the <code>Graphics</code> 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 <code>Insets</code> 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
- * <code>popup</code> is a popup menu.
- *
- * @param popup a <code>JPopupMenu</code>
- * @return true if <code>popup</code>
- */
- 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 <code>JPopupMenu</code>.
- * 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 <code>null</code>.
- *
- * @return a string representation of this <code>JPopupMenu</code>.
- */
- 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
- * <code>JPopupMenu</code> class. It provides an implementation of the
- * Java Accessibility API appropriate to popup menu user-interface
- * elements.
- */
- 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 evt A <code>PropertyChangeEvent</code> 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 values = new Vector();
-
- s.defaultWriteObject();
- // Save the invoker, if its Serializable.
- if(invoker != null && invoker instanceof Serializable) {
- values.addElement("invoker");
- values.addElement(invoker);
- }
- // Save the popup, if its Serializable.
- if(popup != null && popup instanceof Serializable) {
- values.addElement("popup");
- values.addElement(popup);
- }
- s.writeObject(values);
-
- if (getUIClassID().equals(uiClassID)) {
- byte count = JComponent.getWriteObjCounter(this);
- JComponent.setWriteObjCounter(this, --count);
- if (count == 0 && ui != null) {
- ui.installUI(this);
- }
- }
- }
-
- // implements javax.swing.MenuElement
- private void readObject(ObjectInputStream s)
- throws IOException, ClassNotFoundException {
- s.defaultReadObject();
-
- Vector values = (Vector)s.readObject();
- int indexCounter = 0;
- int maxCounter = values.size();
-
- if(indexCounter < maxCounter && values.elementAt(indexCounter).
- equals("invoker")) {
- invoker = (Component)values.elementAt(++indexCounter);
- indexCounter++;
- }
- if(indexCounter < maxCounter && values.elementAt(indexCounter).
- equals("popup")) {
- popup = (Popup)values.elementAt(++indexCounter);
- indexCounter++;
- }
- }
-
-
- /**
- * This method is required to conform to the
- * <code>MenuElement</code> interface, but it not implemented.
- * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)
- */
- public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {}
-
- /**
- * Processes a key event forwarded from the
- * <code>MenuSelectionManager</code> and changes the menu selection,
- * if necessary, by using <code>MenuSelectionManager</code>'s API.
- * <p>
- * Note: you do not have to forward the event to sub-components.
- * This is done automatically by the <code>MenuSelectionManager</code>.
- *
- * @param e a <code>KeyEvent</code>
- * @param path the <code>MenuElement</code> path array
- * @param manager the <code>MenuSelectionManager</code>
- */
- public void processKeyEvent(KeyEvent e, MenuElement path[],
- MenuSelectionManager manager) {
- MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(),
- e.getWhen(), e.getModifiers(),
- e.getKeyCode(), e.getKeyChar(),
- path, manager);
- processMenuKeyEvent(mke);
-
- if (mke.isConsumed()) {
- e.consume();
- }
- }
-
- /**
- * Handles a keystroke in a menu.
- *
- * @param e a <code>MenuKeyEvent</code> object
- * @since 1.5
- */
- private void processMenuKeyEvent(MenuKeyEvent e) {
- switch (e.getID()) {
- case KeyEvent.KEY_PRESSED:
- fireMenuKeyPressed(e); break;
- case KeyEvent.KEY_RELEASED:
- fireMenuKeyReleased(e); break;
- case KeyEvent.KEY_TYPED:
- fireMenuKeyTyped(e); break;
- default:
- break;
- }
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type.
- *
- * @param event a <code>MenuKeyEvent</code>
- * @see EventListenerList
- */
- private void fireMenuKeyPressed(MenuKeyEvent event) {
- Object[] listeners = listenerList.getListenerList();
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==MenuKeyListener.class) {
- ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event);
- }
- }
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type.
- *
- * @param event a <code>MenuKeyEvent</code>
- * @see EventListenerList
- */
- private void fireMenuKeyReleased(MenuKeyEvent event) {
- Object[] listeners = listenerList.getListenerList();
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==MenuKeyListener.class) {
- ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event);
- }
- }
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type.
- *
- * @param event a <code>MenuKeyEvent</code>
- * @see EventListenerList
- */
- private void fireMenuKeyTyped(MenuKeyEvent event) {
- Object[] listeners = listenerList.getListenerList();
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==MenuKeyListener.class) {
- ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event);
- }
- }
- }
-
- /**
- * Messaged when the menubar selection changes to activate or
- * deactivate this menu. This implements the
- * <code>javax.swing.MenuElement</code> interface.
- * Overrides <code>MenuElement.menuSelectionChanged</code>.
- *
- * @param isIncluded true if this menu is active, false if
- * it is not
- * @see MenuElement#menuSelectionChanged(boolean)
- */
- public void menuSelectionChanged(boolean isIncluded) {
- if (DEBUG) {
- System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded);
- }
- if(invoker instanceof JMenu) {
- JMenu m = (JMenu) invoker;
- if(isIncluded)
- m.setPopupMenuVisible(true);
- else
- m.setPopupMenuVisible(false);
- }
- if (isPopupMenu() && !isIncluded)
- setVisible(false);
- }
-
- /**
- * Returns an array of <code>MenuElement</code>s containing the submenu
- * for this menu component. It will only return items conforming to
- * the <code>JMenuElement</code> interface.
- * If popup menu is <code>null</code> returns
- * an empty array. This method is required to conform to the
- * <code>MenuElement</code> interface.
- *
- * @return an array of <code>MenuElement</code> objects
- * @see MenuElement#getSubElements
- */
- public MenuElement[] getSubElements() {
- MenuElement result[];
- Vector tmp = new Vector();
- int c = getComponentCount();
- int i;
- Component m;
-
- for(i=0 ; i < c ; i++) {
- m = getComponent(i);
- if(m instanceof MenuElement)
- tmp.addElement(m);
- }
-
- result = new MenuElement[tmp.size()];
- for(i=0,c=tmp.size() ; i < c ; i++)
- result[i] = (MenuElement) tmp.elementAt(i);
- return result;
- }
-
- /**
- * Returns this <code>JPopupMenu</code> component.
- * @return this <code>JPopupMenu</code> object
- * @see MenuElement#getComponent
- */
- public Component getComponent() {
- return this;
- }
-
-
- /**
- * A popup menu-specific separator.
- */
- static public class Separator extends JSeparator
- {
- public Separator( )
- {
- super( JSeparator.HORIZONTAL );
- }
-
- /**
- * Returns the name of the L&F class that renders this component.
- *
- * @return the string "PopupMenuSeparatorUI"
- * @see JComponent#getUIClassID
- * @see UIDefaults#getUI
- */
- public String getUIClassID()
- {
- return "PopupMenuSeparatorUI";
-
- }
- }
-
- /**
- * Returns true if the <code>MouseEvent</code> is considered a popup trigger
- * by the <code>JPopupMenu</code>'s currently installed UI.
- *
- * @return true if the mouse event is a popup trigger
- * @since 1.3
- */
- public boolean isPopupTrigger(MouseEvent e) {
- return getUI().isPopupTrigger(e);
- }
- }
-