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