JDK-4791569 : Accelerator keys don't work when JPopupMenu is outside its parent
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.1,1.4.2_05
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,windows_2000
  • CPU: generic,x86
  • Submitted: 2002-12-10
  • Updated: 2004-10-13
  • Resolved: 2003-04-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availabitlity Release.

To download the current JDK release, click here.
Other
1.4.2_07 b01Fixed
Related Reports
Duplicate :  
Relates :  
Description
Name: jk109818			Date: 12/10/2002


FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
JPopupMenuItem accelerator keys (set thanks Command) are
nolonger active as soon as the JPopupMenu is (even
partially) outside the listener area.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.Right click the mouse button to trigger a PopupMenu so the
popup is partially outside its parent
2.Try to type an accelerator command
3.The command is not effective

(note that the keyboard arrows are still effective)

EXPECTED VERSUS ACTUAL BEHAVIOR :
The accelerator command should be effective

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package sample.popup;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;

/**
 * @author f.savino
 *
 * To change this generated comment edit the template variable "typecomment":
 * Window>Preferences>Java>Templates.
 * To enable and disable the creation of type comments go to
 * Window>Preferences>Java>Code Generation.
 */

public class PopupSample extends JPanel {
	private final String _lstModel[] = { "First Item", "Second Item", "Third Item" };
	private JList _myList = null;
	private JScrollPane _jsp = null;
	private JPopupMenu _myPopupMenu = null;
	private Action _actCut = null;
	private Action _actCopy = null;
	private Action _actPaste = null;
	
	public PopupSample() {
		initMembers();
		initLayout();
		initListener();
	}

	/**
	 * Method initMembers.
	 */
	private void initMembers() {
		_myList = new JList(_lstModel);
		_myPopupMenu = new JPopupMenu("MyPopup");
		// Cut menu item
		_actCut = new AbstractAction("cut") {
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(PopupSample.this.getTopLevelAncestor(), "Cut");
			}
		};
        _actCut.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK));
        _myPopupMenu.add(_actCut);
        
		// Copy menu item
		_actCopy = new AbstractAction("Copy") {
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(PopupSample.this.getTopLevelAncestor(), "Copy");
			}
		};
        _actCopy.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
        _myPopupMenu.add(_actCopy);

		// Paste menu item
		_actPaste = new AbstractAction("Paste") {
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(PopupSample.this.getTopLevelAncestor(), "Paste");
			}
		};
        _actPaste.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK));
        _myPopupMenu.add(_actPaste);
        
        _jsp = new JScrollPane(_myList);
	}


	/**
	 * Method initLayout.
	 */
	private void initLayout() {
		this.setLayout(new BorderLayout());
		this.add(new JLabel("Right click in the control above..."), BorderLayout.NORTH);
		this.add(_jsp, BorderLayout.CENTER);
	}
	
	/**
	 * Method initListener.
	 */
	private void initListener() {
		_myList.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				maybeShowPopup(e);
			}
			public void mouseReleased(MouseEvent e) {
				maybeShowPopup(e);
			}
			private void maybeShowPopup(MouseEvent e) {
				if (e.isPopupTrigger()) {
					_myPopupMenu.show(e.getComponent(), e.getX(), e.getY());
				}
			}
		});
		_jsp.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				maybeShowPopup(e);
			}
			public void mouseReleased(MouseEvent e) {
				maybeShowPopup(e);
			}
			private void maybeShowPopup(MouseEvent e) {
				if (e.isPopupTrigger()) {
					_myPopupMenu.show(e.getComponent(), e.getX(), e.getY());
				}
			}
		});
	}

	public static void main(String[] args) {
		JFrame frm = new JFrame();
		frm.setTitle("ContextMenu");
		frm.getContentPane().add(new PopupSample());
		// Not 1.2 complient !!!
		frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frm.pack();
		frm.show();
	}
}

---------- END SOURCE ----------
(Review ID: 178961) 
======================================================================
###@###.### 10/13/04 17:04 GMT

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.4.2_07 tiger FIXED IN: 1.4.2_07 tiger INTEGRATED IN: 1.4.2_07 tiger tiger-b04
2004-10-02

SUGGESTED FIX ###@###.### 2003-03-20 *** /net/diablo/export2/swing/zpm/webrev/src/share/classes/javax/swing/KeyboardManager.java- Wed Feb 19 13:40:49 2003 --- KeyboardManager.java Wed Feb 19 13:37:19 2003 *************** *** 114,120 **** */ private static Container getTopAncestor(JComponent c) { for(Container p = c.getParent(); p != null; p = p.getParent()) { ! if (p instanceof Window || p instanceof Applet || p instanceof JInternalFrame) { return p; } } --- 114,122 ---- */ private static Container getTopAncestor(JComponent c) { for(Container p = c.getParent(); p != null; p = p.getParent()) { ! if (p instanceof Window && ((Window)p).isFocusableWindow() || ! p instanceof Applet || p instanceof JInternalFrame) { ! return p; } } *** /net/diablo/export2/swing/zpm/webrev/src/share/classes/javax/swing/PopupFactory.java- Wed Feb 19 13:40:48 2003 --- PopupFactory.java Wed Feb 19 13:36:33 2003 *************** *** 492,504 **** Component component = getComponent(); if(owner != null && component != null) { Window w = SwingUtilities.getWindowAncestor(owner); ! if(w == null) return false; Window[] ownedWindows = w.getOwnedWindows(); if(ownedWindows != null) { Rectangle bnd = component.getBounds(); for(int i=0; i<ownedWindows.length;i++) { ! if(bnd.intersects(ownedWindows[i].getBounds())) { return true; } } --- 492,508 ---- Component component = getComponent(); if (owner != null && component != null) { Window w = SwingUtilities.getWindowAncestor(owner); ! if (w == null) { return false; + } Window[] ownedWindows = w.getOwnedWindows(); if (ownedWindows != null) { Rectangle bnd = component.getBounds(); for (int i=0; i<ownedWindows.length;i++) { ! Window owned = ownedWindows[i]; ! if (owned.isVisible() && ! bnd.intersects(owned.getBounds())) { ! return true; } }
2004-10-02

EVALUATION Name: azR10139 Date: 12/23/2002 We are hendling the accelerators in the InputMap that we are setting for JMenuItem with WHEN_IN_FOCUSED_WINDOW mask. But when menuitem to which this accelerator is set becames member of the heavyweigth popup they are no longer in the focused window and accelerator stops working. The idea of suggested fix is to handle keyboard accelerator in the BasicMenuItemUI$MenuKeyHandler.menuKeyPressed() method to catch accelerator in the heavyweigth popup. ###@###.### 12/23/2002 ====================================================================== Name: pzR10082 Date: 02/19/2003 Those accelerators are actually handled using Actions. The problem is that those Actions are never invoked after a Dialog has been displayed. The method PopupFactory.overlappedByOwnedWindow() is at fault. When checking whether popup overlaps any of the owned Windows, it doesn't check visibility of those Windows. When it encounters a Dialog (even invisible) it finds that it overlaps popup and forces heavyweight popups. Heavyweight popups do not process KeyStrokes because they are non-focusable Windows. So we should register Actions with the Frame rather than with HeavyWeigthWindow. Method KeyboardManager.getTopAncestor() should be modified accordingly. ###@###.### 2003-02-19 ======================================================================
2003-02-19