JDK-4472032 : Focus Problem: JMenu with mnemonic + holizontal allow key can't work on swing te
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.0
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris,windows_2000
  • CPU: x86,sparc
  • Submitted: 2001-06-20
  • Updated: 2001-09-20
  • Resolved: 2001-09-06
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.
Other
1.4.0 beta3Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
Tested with Merlin beta-refresh b68 on Solaris8,9/Sparc ja locale and Windows2000 and Windows98;


JMenu with mnemonic + holizontal allow key can't work properly on swing text component.
For example, 
opening "File" menu with "Alt + F" and trying to re-select other JMenu, "Edit" menu with horizontal allow key, "Edit" menu can be opened, however, a focus is no more on the menu, but moves onto the text component.
Therefore, any menu item of "Edit" menu can not be selected with vertical allow keys.

For example,

1. Compile attached program, JTextAreaTest.java and UIManager.java
2. Launch JTextAreaTest.
3. Press "Alt + F" keys to open "File" menu.
4. Press a right allow key to select "Edit" menu.
   Then, "Edit" menu is opened, however, a focus moved onto JTextArea and  cursol is now on the JTextArea, therefore, any menu items of "Edit" menu can not be selected.

JTextFieldTest.java is also attached and this problem is reproducible with JTextFieldTest.


This is not reproducible with Merlin-beta and jdk1.3.1.
Therefore, this is a regression.
This is reproducible on C locale.


===============================================================================

Testing with b74, I confirmed that this bug is not reproducible on Solaris8, 9 Sparc.

However, I confirmed that the problem is still reproducible on Windows 98 and 2000 only with Windows L&F.
(not reproducible with Metal or CDE/Motif L&F).

miki.tokunaga@japan 2001-08-03

===============================================================================

Regarding the problem that this bug still occurs on Windows using Windows L&F;

Testing with b75 on WinNT, 2K, ME and 98 using Windows L&F, it turned out that this problem does not occur only on Windows NT, but occurs on Win2K, ME and 98.

Therefore, this is not verified completely.

###@###.### 2001-08-15

===============================================================================

I confirmed that this is not reproducible on Solaris8/Sparc, Solaris8/X86 and RH linux 7.1 with b79.

However, as same as in b75, this problem still occurs on 2K, ME and 98 using Windows L&F, but does not occur on WinNT.
This is seen also on Win XP.

Therefore, this is not verified completely.

###@###.### 2001-09-14

===============================================================================

Testing with b80 on Windows ME, 2k, XP and 98, I confirmed that the problem with Windows L&F which was seen until b79 on most version of Windows is not reproducible any more.
This bug's fix is verified.

###@###.### 2001-09-20
===============================================================================


Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: merlin-beta3 FIXED IN: merlin-beta3 INTEGRATED IN: merlin-beta3 VERIFIED IN: merlin-beta3
14-06-2004

EVALUATION This regresion was introduced in b67 and seems to be a problem on Windows only. The bug doesn't have anything to do with JTextFields or JTextAreas. Rather, the focus on the lightweight popup menu is transferred to the first focusable component on the panel when the right or left arrow keys are used to select another menu. This bug doesn't seem to affect heavyweight popups. I've attached a simpler test case: PopupFocusBug.java. You will notice that the focus rect is not drawn on Button One when all the popups are heavyweight. When you expand the size of the frame to completely enclose the popup portion of the menu then the focus will be transfered to Button One when you use the right or left arrow key to traverse through the menus. I'm going to reassign to AWT since this regression probably occured as a side effect of all the focus fixes for b67. mark.davidson@Eng 2001-06-21 In the hopes of helping track this down, here is the steps that are happening: . when the menu becomes visible focus is transfered to the JPopupMenu, the current focus owner is recorded. . An ancestor* of the popup menu is removed from the containment hiearchy, resulting in focus being transfered (via the nextFocusHelper method) . the hidden popup menu installs focus on the recorded focus owner (the button). . a new popup menu comes up resulting in requesting focus on the newly visible JPopupMenu. * JPopupMenu uses a Popup (doesn't descend from Component, but will choose the appropriate type of Component subclass based on space and the developers requirement) to display itself. In the case where the popup doesn't fit in the containing window, a Window is used to host the JPopupMenu vs a JPanel if the JPopupMenu fits. scott.violet@eng 2001-06-29 Name: osR10079 Date: 07/06/2001 This regression is intoduced by implementation of new focus request architecture. When we move from one menu(A) to another(B) (by left or right arrows or by mouse) we have following events sequence: 1. menu A hides and swing requests focus for previous focus owner component C (it could be any kind of component, not only JTextField or JTextArea). 2. As a result focus request for C we post two events: FOCUS_LOST for A and FOCUS_GAINED for C and add appropriate HeavyweightFocusRequest (D). (see KeyboardFocusManager::shouldNativelyFocusHeavyweight()) 3. in removeNotify() for A we remove from EventQueue all events with source A. Also we try request focus on "next" component after A (this is C), and since this request is successfully proceeded we do not clear focus owner. 4. we show menu B and request focus for it. Since we already have HeavyweightFocusRequest(D) for our heavyweight we just add LightweightFocusRequest to D. 5. we receive FOCUS_GAINED for C. And at this moment we have focus owner equals to A. But KeyboardFocusManager::retargetFocusEvent() supposed that FOCUS_GAINED should be received only when focus owner is null (because we suppose that appropriate FOCUS_LOST should be received before (this is wrong assumption)). So, we think that this is unexpected focus event and we clear information about all requests. In result, we set focus on C and show B :( this is bad ;) To fix this problem we can: 1. do not remove events for A in removeNotify(). But this needs more investigation, since this change looks very risky and could introduce other regressions. 2. add in retargetFocusEvent() code to correct handling of this case. In this case we should remember about FOCUS_LOST event which will be generated by DefaultKeyboardFocusmanager::dispatchEvent(), it also will be recognized as unexpected by retargetFocusEvent() and it should be handled in special manner too. ###@###.### 6 Jul 2001 ====================================================================== Name: dkR10074 Date: 07/20/2001 ###@###.### 2001-07-20 jdk1.3 - the bug is reproducible jdk1.3.1 - the bug is reproducible jdk1.4-b69 - the bug is reproducible ====================================================================== We had to back out this fix because it introduced 4492880. We need to come up with a new fix that does not cause a deadlock. I believe ###@###.### is working on one, and martak@eng is acting as reviewer. ###@###.### 2001-08-20
11-06-2004

SUGGESTED FIX Name: osR10079 Date: 07/26/2001 ###@###.### 26 Jul 2001 ------- KeyboardFocusManager.java ------- *** /tmp/d7DaySD Thu Jul 26 13:28:54 2001 --- KeyboardFocusManager.java Wed Jul 25 14:17:31 2001 *************** *** 2183,2188 **** --- 2183,2191 ---- return (Window)activeWindow; } } + + static Component newFocusOwner = null; + static AWTEvent retargetFocusEvent(AWTEvent event) { if (clearingCurrentLightweightRequests) { return event; *************** *** 2190,2229 **** KeyboardFocusManager manager = getCurrentKeyboardFocusManager(); ! if (currentLightweightRequests != null) { ! try { ! clearingCurrentLightweightRequests = true; ! for (Iterator iter = currentLightweightRequests.iterator(); ! iter.hasNext(); ) { ! Component currentFocusOwner = manager. ! getGlobalFocusOwner(); ! if (currentFocusOwner == null) { ! // If this ever happens, a focus change has been ! // rejected. Stop generating more focus changes. ! break; ! } ! LightweightFocusRequest lwFocusRequest = ! (LightweightFocusRequest)iter.next(); ! FocusEvent currentFocusOwnerEvent = ! new FocusEvent(currentFocusOwner, ! FocusEvent.FOCUS_LOST, ! lwFocusRequest.temporary, ! lwFocusRequest.component); ! FocusEvent newFocusOwnerEvent = ! new FocusEvent(lwFocusRequest.component, ! FocusEvent.FOCUS_GAINED, ! lwFocusRequest.temporary, ! currentFocusOwner); ! currentFocusOwner.dispatchEvent(currentFocusOwnerEvent); ! lwFocusRequest.component. ! dispatchEvent(newFocusOwnerEvent); } - } finally { - clearingCurrentLightweightRequests = false; - currentLightweightRequests = null; } } --- 2193,2258 ---- KeyboardFocusManager manager = getCurrentKeyboardFocusManager(); ! synchronized(heavyweightRequests) { ! /* ! * This code handles FOCUS_LOST event which is generated by ! * DefaultKeyboardFocusManager for FOCUS_GAINED. ! * ! * This code based on knowledge of DefaultKeyboardFocusManager's ! * implementation and might be not applicable for another ! * KeyboardFocusManager. ! * ! * Fix for 4472032 ! */ ! if (newFocusOwner != null && ! event.getID() == FocusEvent.FOCUS_LOST) ! { ! FocusEvent fe = (FocusEvent)event; ! ! if (manager.getGlobalFocusOwner() == fe.getComponent() && ! fe.getOppositeComponent() == newFocusOwner) { ! newFocusOwner = null; ! return event; ! } ! } ! } ! synchronized(heavyweightRequests) { ! if (currentLightweightRequests != null) { ! try { ! clearingCurrentLightweightRequests = true; ! for (Iterator iter = currentLightweightRequests.iterator(); ! iter.hasNext(); ) ! { ! Component currentFocusOwner = manager. ! getGlobalFocusOwner(); ! if (currentFocusOwner == null) { ! // If this ever happens, a focus change has been ! // rejected. Stop generating more focus changes. ! break; ! } ! LightweightFocusRequest lwFocusRequest = ! (LightweightFocusRequest)iter.next(); ! FocusEvent currentFocusOwnerEvent = ! new FocusEvent(currentFocusOwner, ! FocusEvent.FOCUS_LOST, ! lwFocusRequest.temporary, ! lwFocusRequest.component); ! FocusEvent newFocusOwnerEvent = ! new FocusEvent(lwFocusRequest.component, ! FocusEvent.FOCUS_GAINED, ! lwFocusRequest.temporary, ! currentFocusOwner); ! currentFocusOwner.dispatchEvent(currentFocusOwnerEvent); ! lwFocusRequest.component. ! dispatchEvent(newFocusOwnerEvent); ! } ! } finally { ! clearingCurrentLightweightRequests = false; ! currentLightweightRequests = null; } } } *************** *** 2290,2296 **** return new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, temporary, lwFocusRequest.component); } else if (id == FocusEvent.FOCUS_GAINED && - currentFocusOwner == null && hwFocusRequest != null && nativeSource == hwFocusRequest.heavyweight) { --- 2319,2324 ---- *************** *** 2303,2310 **** (LightweightFocusRequest)hwFocusRequest. lightweightRequests.removeFirst(); boolean temporary = (opposite == null || ! focusedWindowChanged(source, opposite)) ? false : lwFocusRequest.temporary; --- 2331,2354 ---- (LightweightFocusRequest)hwFocusRequest. lightweightRequests.removeFirst(); + Component newSource = lwFocusRequest.component; + if (currentFocusOwner != null) { + /* + * Since we receive FOCUS_GAINED when current focus + * owner is not null, correcponding FOCUS_LOST is supposed + * to be lost. And so, we keep new focus owner + * to determine synthetic FOCUS_LOST event which will be + * generated by KeyboardFocusManager for this FOCUS_GAINED. + * + * This code based on knowledge of + * DefaultKeyboardFocusManager's implementation and might + * be not applicable for another KeyboardFocusManager. + */ + newFocusOwner = newSource; + } + boolean temporary = (opposite == null || ! focusedWindowChanged(newSource, opposite)) ? false : lwFocusRequest.temporary; *************** *** 2315,2321 **** // 'opposite' will be fixed by // DefaultKeyboardFocusManager.realOppositeComponent ! return new FocusEvent(lwFocusRequest.component, FocusEvent.FOCUS_GAINED, temporary, opposite); } --- 2359,2365 ---- // 'opposite' will be fixed by // DefaultKeyboardFocusManager.realOppositeComponent ! return new FocusEvent(newSource, FocusEvent.FOCUS_GAINED, temporary, opposite); } ======================================================================
11-06-2004