United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4150021 : Frame cannot handle keyboard events (keyTyped) if buttons are added to frame.

Details
Type:
Bug
Submit Date:
1998-06-17
Status:
Resolved
Updated Date:
2003-04-12
Project Name:
JDK
Resolved Date:
2002-08-22
Component:
client-libs
OS:
solaris_2.5.1,generic,windows_98,windows_2000
Sub-Component:
java.awt
CPU:
x86,sparc,generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.1.3,1.4.0
Fixed Versions:
1.4.2 (mantis)

Related Reports
Duplicate:
Relates:
Relates:

Sub Tasks

Description

Name: jtC48319			Date: 06/17/98


Steps to recreate problem.

This is a simple program that changes the 
backroung color of the frame.  You have two 
options; yellow and blue.

Press 'y' for yellow and 'b' for blue.
Nothing happens.  If you click the buttons with
the mouse, everythings works as expected.  It
appears that the frame can never regain focus to
accept key event(as defined in Core Java 1.1
Foundations).  I tried to get the focus for the 
frame by using the requestFocus() method, but it
does nothing(which I noticed was already reported).

NOTE:  We are going through e-mail updates.  If
the provided e-mail is return, please send all 
future correspondence to ###@###.###.
Thank you.
 
Here is the code:

import java.awt.*;
import java.awt.event.*;

public class ButtonTest extends Frame
{

public ButtonTest()
{
  addWindowListener(new WindowAdapter()
  {
   public void windowClosing(WindowEvent e)
   {System.exit(0);}
  } );

  setSize(300, 200);
  setTitle("Button Test");
  setLayout(new FlowLayout());

  //Create button, add event handler, and add to frame
  Button yellow = new Button("Yellow");
  yellow.addActionListener(new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {setBackground(Color.yellow);}
  } );
  add(yellow);

  //Create button, add event handler, and add to frame
  Button blue = new Button("Blue");
  blue.addActionListener(new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {setBackground(Color.blue);}
  } );
  add(blue);

  //I can never pass through this callback,
  //the frame never seems to have focus    
  addKeyListener(new KeyAdapter()
  {
   public void keyTyped(KeyEvent e)
   { ButtonTest.this.keyTyped(e); }
  } );

  //Tried using this method to gain focus for frame
  //but it does not work.
  requestFocus();
}

public void keyTyped(KeyEvent evt)
{
  char keyChar = Character.toLowerCase(evt.getKeyChar());
  if (keyChar == 'y') setBackground(Color.yellow);
  if (keyChar == 'b') setBackground(Color.blue);
}

public static void main(String args[])
{
  Frame f = new ButtonTest();
  f.show();
}

}
(Review ID: 25346)
======================================================================

                                    

Comments
SUGGESTED FIX



Name: osR10079			Date: 08/12/2002

The fix for problem with requesting focus just after
showing frame.

------- KeyboardFocusManager.java -------
*** /tmp/sccs.HEaGeu    Tue Jul  2 12:01:33 2002
--- KeyboardFocusManager.java   Tue Jun 25 17:49:51 2002
***************
*** 1609,1623 ****
        }
      }
      static synchronized void setMostRecentFocusOwner(Window window,
!                                                    Component component) {
!       // ATTN: component has a strong reference to window via chain
!       // of Component.parent fields.  Since WeakHasMap refers to its
!       // values strongly, we need to break the strong link from the
!       // value (component) back to its key (window).
!       WeakReference weakValue = null;
!       if (component != null) {
!           weakValue = new WeakReference(component);
!       }
          mostRecentFocusOwners.put(window, weakValue);
      }
      static void clearMostRecentFocusOwner(Component comp) {
--- 1609,1623 ----
        }
      }
      static synchronized void setMostRecentFocusOwner(Window window,
!                                                      Component component) {
!         // ATTN: component has a strong reference to window via chain
!         // of Component.parent fields.  Since WeakHasMap refers to its
!         // values strongly, we need to break the strong link from the
!         // value (component) back to its key (window).
!         WeakReference weakValue = null;
!         if (component != null) {
!             weakValue = new WeakReference(component);
!         }
          mostRecentFocusOwners.put(window, weakValue);
      }
      static void clearMostRecentFocusOwner(Component comp) {
***************
*** 1634,1649 ****
              }
          }
  
!         if ((window != null) 
!             && (getMostRecentFocusOwner((Window)window) == comp)) 
!         {
!             setMostRecentFocusOwner((Window)window, null);
          }
      }
      static synchronized Component getMostRecentFocusOwner(Window window) {
!       WeakReference weakValue =
!           (WeakReference)mostRecentFocusOwners.get(window);
!       return weakValue == null ? null : (Component)weakValue.get();
      }
  
      /**
--- 1634,1651 ----
              }
          }
  
!         synchronized (KeyboardFocusManager.class) {
!             if ((window != null) 
!                 && (getMostRecentFocusOwner((Window)window) == comp)) 
!             {
!                 setMostRecentFocusOwner((Window)window, null);
!             }
          }
      }
      static synchronized Component getMostRecentFocusOwner(Window window) {
!         WeakReference weakValue =
!             (WeakReference)mostRecentFocusOwners.get(window);
!         return weakValue == null ? null : (Component)weakValue.get();
      }
  
      /**

------- DefaultKeyboardFocusManager.java -------
*** /tmp/sccs.IEaGeu    Tue Jul  2 12:01:33 2002
--- DefaultKeyboardFocusManager.java    Tue Jul  2 11:54:04 2002
***************
*** 254,274 ****
                    }
                }
  
-               // Identify which Component should initially gain focus in
-               // the Window.
-               //
-               // * If we're in SendMessage, then this is a synthetic
-               //   WINDOW_GAINED_FOCUS message which was generated by a
-               //   the FOCUS_GAINED handler. Allow the Component to which the
-               //   FOCUS_GAINED message was targeted to receive the focus.
-               // * Otherwise, look up the correct Component here. If we
-               //   perform the lookup after the call to setGlobalFocusOwner,
-               //   'null' will be returned.
-                 Component requestingComp = getCurrentWaitingRequest(newFocusedWindow);
-               Component toFocus = (inSendMessage == 0)
-                     ? newFocusedWindow.getMostRecentFocusOwner()
-                     : null;                
- 
                setGlobalFocusedWindow(newFocusedWindow);
  
                if (newFocusedWindow != getGlobalFocusedWindow()) {
--- 254,259 ----
***************
*** 285,298 ****
                // Make sure that the focus change request doesn't change the
                // focused Window in case we are no longer the focused Window
                // when the request is handled.
                  if (requestingComp != null) {
                      FocusEvent defGain = new FocusEvent(requestingComp, FocusEvent.FOCUS_GAINED,
                                                          false);
                      sendMessage(requestingComp, defGain);
!                 } else if (toFocus != null) {
!                   toFocus.requestFocusInWindow();
!               }
  
                Window realOppositeWindow = this.realOppositeWindow;
                if (realOppositeWindow != we.getOppositeWindow()) {
                    we = new WindowEvent(newFocusedWindow,
--- 270,313 ----
                // Make sure that the focus change request doesn't change the
                // focused Window in case we are no longer the focused Window
                // when the request is handled.
+                 Component requestingComp = getCurrentWaitingRequest(newFocusedWindow);
                  if (requestingComp != null) {
                      FocusEvent defGain = new FocusEvent(requestingComp, FocusEvent.FOCUS_GAINED,
                                                          false);
                      sendMessage(requestingComp, defGain);
!                 } else if (inSendMessage == 0) {
!                     // Identify which Component should initially gain focus 
!                     // in the Window.
!                     //
!                     // * If we're in SendMessage, then this is a synthetic
!                     //   WINDOW_GAINED_FOCUS message which was generated by a
!                     //   the FOCUS_GAINED handler. Allow the Component to 
!                     //   which the FOCUS_GAINED message was targeted to 
!                     //   receive the focus.
!                     // * Otherwise, look up the correct Component here. 
!                     //   We don't use Window.getMostRecentFocusOwner because
!                     //   window is focused now and 'null' will be returned
  
+ 
+                     // Calculating of most recent focus owner and focus 
+                     // request should be synchronized on KeyboardFocusManager.class
+                     // to prevent from thread race when user will request
+                     // focus between calculation and our request.
+                     // But if focus transfer is synchronous, this synchronization
+                     // may cause deadlock, thus we don't synchronize this block.
+                     Component toFocus = KeyboardFocusManager.
+                         getMostRecentFocusOwner(newFocusedWindow);
+                     if ((toFocus == null) && 
+                         newFocusedWindow.isFocusableWindow()) 
+                     {
+                         toFocus = newFocusedWindow.getFocusTraversalPolicy().
+                             getInitialComponent(newFocusedWindow);
+                     }
+                     if (toFocus != null) {
+                         toFocus.requestFocusInWindow();
+                     }
+                 }
+ 
                Window realOppositeWindow = this.realOppositeWindow;
                if (realOppositeWindow != we.getOppositeWindow()) {
                    we = new WindowEvent(newFocusedWindow,


======================================================================
                                     
2004-08-24
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
mantis
mantis-b02

FIXED IN:
mantis
mantis-b02

INTEGRATED IN:
mantis
mantis-b02


                                     
2004-08-24
EVALUATION

eric.hawkes@eng 1998-06-18
This bug sounds as though it has the same root cause as 4033894 
and/or 4027897.  The problem in each case seems to be that the 
frame cannot regain focus after it has lost it.  



Name: rpR10076			Date: 11/16/2001

commit to hopper and tiger
======================================================================

Name: osR10079			Date: 12/21/2001

In the test user tries to listen KeyEvents on Frame.  For
this reason KeyListener is registered  on the frame, and
requestFocus() is called on the frame. 
But requestFocus() is called before frame showing, and AWT rejects
this call because the frame is not visible.  Thus after application
start focus is placed on button (initial component), not on the frame
itself. 

To fix this problem we need to call requestFocus() after
setVisible(true).  But here we have other problem:
requestFocus() is called on main thread, and at the same time on EDT
DefaultKeyboardFocusManager dispatches WINDOW_GAINED_FOCUS (which
comes as a result of setVisible(true)) and in this
handler it determines component which should be a focus owner and
requests focus on this component.  To find out this component, DKFM
call getMostRecentFocusOwner() method on focused window.

Thus we have the following situation:
1. On EDT we call Window.getMostRecentFocusOwner(), which returns
the button as new focus owner, since it is initial component for this
frame.
2. On main thread we call requestFocus() on the frame.
3. On EDT  we call requstFocus() on the button.
And after that the button becomes a focus owner.

So, user expects the frame to be a focus owner and we try to do so
(for this reason we have most recent focus owner, which we update in
requestFocus()).  But because of the thread race and the fact that our
code is not thread safe, focus is placed on the button.

###@###.### 21 Dec 2001
======================================================================

Name: osR10079			Date: 08/12/2002


======================================================================
                                     
2004-08-24
WORK AROUND



Name: jtC48319			Date: 06/17/98


It appears that when the buttons are added to the
frame that the buttons maintain the focus.  Meaning
if there are buttons on the frame, the frame will 
not accept keyboard events.

If the buttons are not installed everything works 
just fine.  The only way I could get the key events
to work was to add a key listner to each of the
buttons.  That way, no matter which button had focus
they each would process a key event.  The code I 
had to add to make this work is as follows and belongs
in the constructor.

yellow.addKeyListener(new KeyAdapter()
{
  public void keyTyped(KeyEvent e)
  {ButtonTest.this.keyTyped(e);}
}

blue.addKeyListener(new KeyAdapter()
{
  public void keyTyped(KeyEvent e)
  {ButtonTest.this.keyTyped(e);}
}  

Now this works, but I do not think this is the way
it should work.  I would think that the button
component should not have to handle all key events.
The frame component should be able to process key
events whether or not it has buttons.

Thank you.


jukka@canada 1998-06-17

The test program will work if keyPressed is used instead of keyTyped.

41,42c41,42
<    public void keyTyped(KeyEvent e)
<    { ButtonTest.this.keyTyped(e); }
---
>    public void keyPressed(KeyEvent e)
>    { ButtonTest.this.keyPressed(e); }
50c50
< public void keyTyped(KeyEvent evt)
---
> public void keyPressed(KeyEvent evt)


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

Name: osR10079			Date: 12/21/2001

We can provide two workarounds for this problem:
1. invoke frame.requestFocus() on EDT by invokeAndWait()/invokeLater().
2. make the frame the initial component by changing FocusTraversalPolicy
   or by calling setFocusable(true) on the frame.
###@###.### 21 Dec 2001
======================================================================
                                     
2004-08-24



Hardware and Software, Engineered to Work Together