--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/JPopupMenu.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1521 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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 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> Swing is not thread safe. For more
+ * information see <a
+ * href="package-summary.html#threading">Swing's Threading
+ * Policy</a>.
+ * <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.
+ *
+ * @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.
+ *
+ * @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;
+ 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
+ screenBounds = gc.getBounds();
+ } else {
+ // If we don't have GraphicsConfiguration use primary screen
+ screenBounds = new Rectangle(toolkit.getScreenSize());
+ }
+
+ Dimension size;
+
+ size = JPopupMenu.this.getPreferredSize();
+
+ // Use long variables to prevent overflow
+ long pw = (long) p.x + (long) size.width;
+ long ph = (long) p.y + (long) size.height;
+
+ if( pw > screenBounds.x + screenBounds.width )
+ p.x = screenBounds.x + screenBounds.width - size.width;
+
+ if( ph > screenBounds.y + screenBounds.height)
+ p.y = screenBounds.y + screenBounds.height - 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>.
+ *
+ * @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() {
+ 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 <code>PropertyChangeListener</code>
+ * which updates the control as changes to the <code>Action</code> 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
+ * <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()) {
+ 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();
+
+ // 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 <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 {@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 <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 e 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);
+ }
+}