1562 lines
51 KiB
Java
1562 lines
51 KiB
Java
/*
|
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package javax.swing;
|
|
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.io.IOException;
|
|
import java.io.ObjectInputStream;
|
|
import java.io.ObjectOutputStream;
|
|
import java.io.Serializable;
|
|
import java.beans.*;
|
|
|
|
import java.util.Locale;
|
|
import java.util.Vector;
|
|
import java.util.Hashtable;
|
|
import javax.accessibility.*;
|
|
import javax.swing.plaf.PopupMenuUI;
|
|
import javax.swing.plaf.ComponentUI;
|
|
import javax.swing.plaf.basic.BasicComboPopup;
|
|
import javax.swing.event.*;
|
|
|
|
import sun.awt.SunToolkit;
|
|
import sun.security.util.SecurityConstants;
|
|
|
|
import java.applet.Applet;
|
|
|
|
/**
|
|
* An implementation of a popup menu -- a small window that pops up
|
|
* and displays a series of choices. A <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="https://docs.oracle.com/javase/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™
|
|
* 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
|
|
*/
|
|
@SuppressWarnings("serial")
|
|
public class JPopupMenu extends JComponent implements Accessible,MenuElement {
|
|
|
|
/**
|
|
* @see #getUIClassID
|
|
* @see #readObject
|
|
*/
|
|
private static final String uiClassID = "PopupMenuUI";
|
|
|
|
/**
|
|
* Key used in AppContext to determine if light way popups are the default.
|
|
*/
|
|
private static final Object defaultLWPopupEnabledKey =
|
|
new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
|
|
|
|
/** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
|
|
static boolean popupPostionFixDisabled = false;
|
|
|
|
static {
|
|
popupPostionFixDisabled = java.security.AccessController.doPrivileged(
|
|
new sun.security.action.GetPropertyAction(
|
|
"javax.swing.adjustPopupLocationToFit","")).equals("false");
|
|
|
|
}
|
|
|
|
transient Component invoker;
|
|
transient Popup popup;
|
|
transient Frame frame;
|
|
private int desiredLocationX,desiredLocationY;
|
|
|
|
private String label = null;
|
|
private boolean paintBorder = true;
|
|
private Insets margin = null;
|
|
|
|
/**
|
|
* Used to indicate if lightweight popups should be used.
|
|
*/
|
|
private boolean lightWeightPopup = true;
|
|
|
|
/*
|
|
* Model for the selected subcontrol.
|
|
*/
|
|
private SingleSelectionModel selectionModel;
|
|
|
|
/* Lock object used in place of class object for synchronization.
|
|
* (4187686)
|
|
*/
|
|
private static final Object classLock = new Object();
|
|
|
|
/* diagnostic aids -- should be false for production builds. */
|
|
private static final boolean TRACE = false; // trace creates and disposes
|
|
private static final boolean VERBOSE = false; // show reuse hits/misses
|
|
private static final boolean DEBUG = false; // show bad params, misc.
|
|
|
|
/**
|
|
* Sets the default value of the <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 popupLocation = new Point(xPosition, yPosition);
|
|
|
|
if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless()) {
|
|
return popupLocation;
|
|
}
|
|
|
|
// Get screen bounds
|
|
Rectangle scrBounds;
|
|
GraphicsConfiguration gc = getCurrentGraphicsConfiguration(popupLocation);
|
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
|
if(gc != null) {
|
|
// If we have GraphicsConfiguration use it to get screen bounds
|
|
scrBounds = gc.getBounds();
|
|
} else {
|
|
// If we don't have GraphicsConfiguration use primary screen
|
|
scrBounds = new Rectangle(toolkit.getScreenSize());
|
|
}
|
|
|
|
// Calculate the screen size that popup should fit
|
|
Dimension popupSize = JPopupMenu.this.getPreferredSize();
|
|
long popupRightX = (long)popupLocation.x + (long)popupSize.width;
|
|
long popupBottomY = (long)popupLocation.y + (long)popupSize.height;
|
|
int scrWidth = scrBounds.width;
|
|
int scrHeight = scrBounds.height;
|
|
|
|
if (!canPopupOverlapTaskBar()) {
|
|
// Insets include the task bar. Take them into account.
|
|
Insets scrInsets = toolkit.getScreenInsets(gc);
|
|
scrBounds.x += scrInsets.left;
|
|
scrBounds.y += scrInsets.top;
|
|
scrWidth -= scrInsets.left + scrInsets.right;
|
|
scrHeight -= scrInsets.top + scrInsets.bottom;
|
|
}
|
|
int scrRightX = scrBounds.x + scrWidth;
|
|
int scrBottomY = scrBounds.y + scrHeight;
|
|
|
|
// Ensure that popup menu fits the screen
|
|
if (popupRightX > (long) scrRightX) {
|
|
popupLocation.x = scrRightX - popupSize.width;
|
|
}
|
|
|
|
if (popupBottomY > (long) scrBottomY) {
|
|
popupLocation.y = scrBottomY - popupSize.height;
|
|
}
|
|
|
|
if (popupLocation.x < scrBounds.x) {
|
|
popupLocation.x = scrBounds.x;
|
|
}
|
|
|
|
if (popupLocation.y < scrBounds.y) {
|
|
popupLocation.y = scrBounds.y;
|
|
}
|
|
|
|
return popupLocation;
|
|
}
|
|
|
|
/**
|
|
* Tries to find GraphicsConfiguration
|
|
* that contains the mouse cursor position.
|
|
* Can return null.
|
|
*/
|
|
private GraphicsConfiguration getCurrentGraphicsConfiguration(
|
|
Point popupLocation) {
|
|
GraphicsConfiguration gc = null;
|
|
GraphicsEnvironment ge =
|
|
GraphicsEnvironment.getLocalGraphicsEnvironment();
|
|
GraphicsDevice[] gd = ge.getScreenDevices();
|
|
for(int i = 0; i < gd.length; i++) {
|
|
if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
|
|
GraphicsConfiguration dgc =
|
|
gd[i].getDefaultConfiguration();
|
|
if(dgc.getBounds().contains(popupLocation)) {
|
|
gc = dgc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// If not found and we have invoker, ask invoker about his gc
|
|
if(gc == null && getInvoker() != null) {
|
|
gc = getInvoker().getGraphicsConfiguration();
|
|
}
|
|
return gc;
|
|
}
|
|
|
|
/**
|
|
* Returns whether popup is allowed to be shown above the task bar.
|
|
*/
|
|
static boolean canPopupOverlapTaskBar() {
|
|
boolean result = true;
|
|
|
|
Toolkit tk = Toolkit.getDefaultToolkit();
|
|
if (tk instanceof SunToolkit) {
|
|
result = ((SunToolkit)tk).canPopupOverlapTaskBar();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 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<Component> tempItems = new Vector<Component>();
|
|
|
|
/* 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 (Component tempItem : tempItems) {
|
|
add(tempItem);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 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 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()) {
|
|
showPopup();
|
|
} else {
|
|
validate();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the visibility of the popup menu.
|
|
*
|
|
* @param b true to make the popup visible, or false to
|
|
* hide it
|
|
* @beaninfo
|
|
* bound: true
|
|
* description: Makes the popup visible
|
|
*/
|
|
public void setVisible(boolean b) {
|
|
if (DEBUG) {
|
|
System.out.println("JPopupMenu.setVisible " + b);
|
|
}
|
|
|
|
// Is it a no-op?
|
|
if (b == isVisible())
|
|
return;
|
|
|
|
// if closing, first close all Submenus
|
|
if (b == false) {
|
|
|
|
// 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
|
|
// a protected method and cannot be called from BasicPopupMenuUI directly
|
|
// The real solution could be to make
|
|
// firePopupMenuCanceled public and call it directly.
|
|
Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled");
|
|
if (doCanceled != null && doCanceled == Boolean.TRUE) {
|
|
putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
|
|
firePopupMenuCanceled();
|
|
}
|
|
getSelectionModel().clearSelection();
|
|
|
|
} else {
|
|
// This is a popup menu with MenuElement children,
|
|
// set selection path before popping up!
|
|
if (isPopupMenu()) {
|
|
MenuElement me[] = new MenuElement[1];
|
|
me[0] = this;
|
|
MenuSelectionManager.defaultManager().setSelectedPath(me);
|
|
}
|
|
}
|
|
|
|
if(b) {
|
|
firePopupMenuWillBecomeVisible();
|
|
showPopup();
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves <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 void showPopup() {
|
|
Popup oldPopup = popup;
|
|
|
|
if (oldPopup != null) {
|
|
oldPopup.hide();
|
|
}
|
|
PopupFactory popupFactory = PopupFactory.getSharedInstance();
|
|
|
|
if (isLightWeightPopupEnabled()) {
|
|
popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
|
|
}
|
|
else {
|
|
popupFactory.setPopupType(PopupFactory.HEAVY_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);
|
|
popup = newPopup;
|
|
newPopup.show();
|
|
}
|
|
|
|
/**
|
|
* Returns true if the popup menu is visible (currently
|
|
* being displayed).
|
|
*/
|
|
public boolean isVisible() {
|
|
return popup != null;
|
|
}
|
|
|
|
/**
|
|
* Sets the location of the upper left corner of the
|
|
* popup menu using x, y coordinates.
|
|
* <p>
|
|
* The method changes the geometry-related data. Therefore,
|
|
* the native windowing system may ignore such requests, or it may modify
|
|
* the requested data, so that the {@code JPopupMenu} object is placed and sized
|
|
* in a way that corresponds closely to the desktop settings.
|
|
*
|
|
* @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)) {
|
|
showPopup();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)) {
|
|
showPopup();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 containers.
|
|
*
|
|
* @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.
|
|
*/
|
|
@SuppressWarnings("serial")
|
|
protected class AccessibleJPopupMenu extends AccessibleJComponent
|
|
implements PropertyChangeListener {
|
|
|
|
/**
|
|
* AccessibleJPopupMenu constructor
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
protected AccessibleJPopupMenu() {
|
|
JPopupMenu.this.addPropertyChangeListener(this);
|
|
}
|
|
|
|
/**
|
|
* Get the role of this object.
|
|
*
|
|
* @return an instance of AccessibleRole describing the role of
|
|
* the object
|
|
*/
|
|
public AccessibleRole getAccessibleRole() {
|
|
return AccessibleRole.POPUP_MENU;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when a bound property is changed.
|
|
* @param e A <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<Object> values = new Vector<Object>();
|
|
|
|
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<MenuElement> tmp = new Vector<MenuElement>();
|
|
int c = getComponentCount();
|
|
int i;
|
|
Component m;
|
|
|
|
for(i=0 ; i < c ; i++) {
|
|
m = getComponent(i);
|
|
if(m instanceof MenuElement)
|
|
tmp.addElement((MenuElement) m);
|
|
}
|
|
|
|
result = new MenuElement[tmp.size()];
|
|
for(i=0,c=tmp.size() ; i < c ; i++)
|
|
result[i] = 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.
|
|
*/
|
|
@SuppressWarnings("serial")
|
|
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);
|
|
}
|
|
}
|