JDK-6249972 : REGRESSION: JMenuItem(String,int) does not handle lower-case mnemonics properly
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2005-04-04
  • Updated: 2011-01-19
  • Resolved: 2006-08-18
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 Availability Release.

To download the current JDK release, click here.
JDK 6
6 b96Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
In versions prior to 5.0, JMenuItem(String,int) accepted a lower-case char for the second parameter and processed it the same as an upper-case. In Java 5 lower-case mnemonics appear to be ignored. But JMenuItem.setMnemonic(char) still accepts lower-case with no problem.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the code below. Type alt-P. As expected, the "Problem" menu drops down. Press the hot key for the first menu item. Then go through the same cycle for the remaining menu items.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected each menu item's action listener to fire when its mnemonic is pressed.
ACTUAL -
Mnemonics set with setMnemonic, with a constructor taking a key event code, or with a constructor taking an upper-case letter, all worked. Mnemonics set with a constructor taking a lower-case letter did not work. Note that calling the constructor with a lower-case letter still managed to highlight that letter in the menu item text, so something recognized that this was a valid mnemonic, but it doesn't fire when the key is pressed.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Dork extends JFrame implements ActionListener
{
	public static void main(String[] args)
	{
		Dork dork=new Dork();
		dork.setVisible(true);
	}
	
	public Dork()
	{
		super("Dork");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		JMenuBar bar=new JMenuBar();
		setJMenuBar(bar);
		
		JMenu menu;
		JMenuItem item;
		
		menu=new JMenu("Good");
		menu.setMnemonic('g');
		bar.add(menu);
		
		item=new JMenuItem("Charity");
		item.setMnemonic('c');
		item.addActionListener(this);
		menu.add(item);
		
		item=new JMenuItem("Java");
		item.setMnemonic('j');
		item.addActionListener(this);
		menu.add(item);
		
		menu=new JMenu("Problem");
		menu.setMnemonic('p');
		bar.add(menu);
		
		item=new JMenuItem("setMnemonic(char)");
		item.setMnemonic('s');
		item.addActionListener(this);
		menu.add(item);
		
		item=new JMenuItem("Constructor(String,KeyEvent)",KeyEvent.VK_K);
		item.addActionListener(this);
		menu.add(item);
		
		item=new JMenuItem("Constructor(String,lc)",'l');
		item.addActionListener(this);
		menu.add(item);
		
		item=new JMenuItem("Constructor(String,UC)",'U');
		item.addActionListener(this);
		menu.add(item);
		
		pack();
		setLocationRelativeTo(null);
	}
	
	public void actionPerformed(ActionEvent e)
	{
		JMenuItem source=(JMenuItem)e.getSource();
		JOptionPane.showMessageDialog(this,source.getText());
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Change the mnemonic to upper-case or a virtual key code, or use setMnemonic.

It's easy enough to work around in principle. The catch is that we have tons of code written for 1.4.2 that used lower case for the mnemonic.

Release Regression From : 1.4.2_05
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.
###@###.### 2005-04-04 11:43:40 GMT

Comments
SUGGESTED FIX +++ BasicPopupMenuUI.java Tue Aug 1 18:24:38 2006 @@ -285,16 +285,17 @@ } menuToOpen = null; } public void menuKeyPressed(MenuKeyEvent e) { + char keyChar = e.getKeyChar(); + // Handle the case for Escape or Enter... - if (!Character.isLetterOrDigit(e.getKeyChar())) { + if (!Character.isLetterOrDigit(keyChar)) { return; } - int keyCode = e.getKeyCode(); MenuSelectionManager manager = e.getMenuSelectionManager(); MenuElement path[] = e.getPath(); MenuElement items[] = popupMenu.getSubElements(); int currentIndex = -1; int matches = 0; @@ -304,12 +305,13 @@ for (int j = 0; j < items.length; j++) { if (! (items[j] instanceof JMenuItem)) { continue; } JMenuItem item = (JMenuItem)items[j]; + int mnemonic = item.getMnemonic(); if (item.isEnabled() && - item.isVisible() && keyCode == item.getMnemonic()) { + item.isVisible() && lower(keyChar) == lower(mnemonic)) { if (matches == 0) { firstMatch = j; matches++; } else { if (indexes == null) { @@ -355,10 +357,18 @@ return; } public void menuKeyReleased(MenuKeyEvent e) { } + + private char lower(char keyChar) { + return Character.toLowerCase(keyChar); + } + + private char lower(int mnemonic) { + return Character.toLowerCase((char) mnemonic); + } } private static class Actions extends UIAction { // Types of actions private static final String CANCEL = "cancel";
12-04-2006

EVALUATION The bug is a regression of the 4670831. Earlier MenuKeyHandler in BasicMenuItemUI had some code which compares key code and mnemonic in the lower case. Now when key code and mnemonic are compared they are not reduced to the lower case (see BasicPopupMenuUI.BasicMenuKeyListener.menuKeyPressed()).
12-04-2006

EVALUATION This regression was introduced by the fix for 4670831. Mnemonics are supposed to be stored as VK codes. JMenuItem has both setMnemonic(int) and setMnemonic(char) methods. The setMnemonic(char) version attempts to convert from a char to a VK code. The setMnemonic(int) method does not, and assumes the int is a key code. The constructor that takes an int for the mnemonic calls the setMnemonic(int) method. In 1.4.2, the mnemonic handling for menu items was forgiving to those who passed a lowercase character to the setMnemonic(int) method because it treated the mnemonic as a char (which is wrong). After the fix to 6249972, the code is more correct, but has caused this regression. More investigation is required. ###@###.### 2005-04-04 16:48:38 GMT
04-04-2005