Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4150021
Votes 43
Synopsis Frame cannot handle keyboard events (keyTyped) if buttons are added to frame.
Category java:classes_awt
Reported Against 1.1.3 , merlin-beta , merlin-beta2
Release Fixed 1.4.2(mantis)
State 10-Fix Delivered, bug
Priority: 4-Low
Related Bugs 4027897 , 4033894 , 4478706
Submit Date 17-JUN-1998
Description




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  xxxxx@xxxxx .
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)
======================================================================
Work Around




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.


 xxxxx@xxxxx  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)


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



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.
 xxxxx@xxxxx  21 Dec 2001
======================================================================
Evaluation
 xxxxx@xxxxx  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.  





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



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  xxxxx  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.

 xxxxx@xxxxx  21 Dec 2001
======================================================================




======================================================================
Comments
  
  Include a link with my name & email   

Submitted On 11-DEC-1998
mleflar
I had a similar situation and found that it works as expected in the win95
environment (jdk1.1.x) but on the Solaris systems it did not...


Submitted On 07-NOV-1999
koester
Actually I've encountered this situation myself, though I am not convinced it
is a bug.
The following code demonstrates a solution to the sequence of actions you
desire:
(JDK 1.1.8)
import java.awt.*;
import java.awt.event.*;

public class test extends Frame
{
   public test()
   {
      add(new myButton());
      addWindowListener(new OnWindow());
      addKeyListener(new OnKey());
      setSize(100, 100);
      setVisible(true);
   }
   private class myButton extends Button
   {
      public myButton()
      {
         super(&quot;Click me&quot;);
         addActionListener(new OnClick());
      }
      private class OnClick implements ActionListener
      {
         public void actionPerformed(ActionEvent e)
         {
            System.out.println(&quot;Insde button handler (requesting frame
focus)&quot;);
            test.this.requestFocus();
         }
      }
   }
   private class OnKey extends KeyAdapter
   {
      public void keyTyped(KeyEvent e)
      {
         System.out.println(&quot;Inside key handler&quot;);
      }
   }
                  
   private class OnWindow extends WindowAdapter
   {
      public void windowActivated(WindowEvent e)
      {
         System.out.println(&quot;Inside window handler (requesting frame
focus)&quot;);
         requestFocus();
      }
      public void windowClosing(WindowEvent e)
      {
         setVisible(false);
         dispose();
         System.exit(0);
      }
   }
                  
   public static void main(String args[])
   {
      new test();
   }
}


Submitted On 20-MAR-2002
soyke
These workarounds don't help much if you are trying to 
capture a key event from anywhere on a screen. For 
instance, we are trying to listen for an F5 key to refresh 
a list no matter where the user (focus) is on a certain 
screen. Adding a listener to 50+ objects on each screen is 
out of the question. I tried using the AWTEventListener, 
but that listens to events system-wide, thus forcing us to 
determine in each eventDispatched() method if the event 
happened in an object on the particular screen we're 
interested in.


Submitted On 31-MAR-2002
phenix61
I agree totally with Soyke. I am trying to detect a series 
of keyEvents that are done anywhere on the screen and 
because I do not have access to all the code, I cannot 
place a KeyListener on every object.


Submitted On 08-APR-2002
dantams
Just found the method registerKeyboardAction() in
JComponent, which takes care of just the mentioned problem.


Submitted On 08-APR-2002
dantams
Here is one workaround from the forum:

>Re: ctrl-key shortcut
>Author: Kleopatra
>In Reply To: ctrl-key shortcut Apr 8, 2002 12:07 AM  
>Reply 2 of 2
>Forget about keyEvents/-Listeners - they are way too low
>level in all except very special situations when using
>Swing. Use the keybindings mechanism instead: put the key
>into the buttons inputMap of type WEN_IN_FOCUSED_WINDOW and
>the action into its actionMap.
>
>Greetings
>Jeanette
Only caveat is that it only works with Java 1.3 and upwards.


Submitted On 08-APR-2002
dantams
Just use the AWTEventListener method and then check with
((Component) Event.getSource()).getRootPane() if it's in the
right window.

It's a bit tedious and low-level, but it works.


Submitted On 13-JUN-2002
mushina
I had written some code where I added KeyEventListeners toa 
a JFrame and the KeyEventListeners stopped working after a 
JButton was clicked (even if no listeners were attatched to 
the button).  According to isFocused(), my frame still had 
focus, but the KeyEventListeners were not being triggered.  

I was, however able to create a workaround by using a 
KeyEventDispatcher.  The KeyEventDispatcher gets called 
before the key gets passed to the component in focus.  This 
allows you to do whatever you want with the key press, 
regardless of which element is in focus.  The relevant code 
looks something like this:

KeyboardFocusManager manager = 
KeyboardFocusManager.getCurrentKeyboardFocusManager();
manager.addKeyEventDispatcher(keyDispatcher);

private KeyEventDispatcher keyDispatcher = new 
KeyEventDispatcher(){
  public boolean dispatchKeyEvent(KeyEvent k) {
    if (key.getID() != KeyEvent.KEY_TYPED) { return false; }
/* Returning false tells the focus manager you did not do
   anything with the key stroke and that it still needs to 
   be dealt with */
    ...  // Do whatever you want to do with the KeyEvent
    return true;
/* Returning true tells the focus manager it should no
   longer worry about this keyEvent*/
};

Personally, I think that the main problem here is that the 
new Focus Manager in 1.4 does not propogate KeyEvents up to 
the Focused Window - it just sends them to the Focused 
Component and that's it.



PLEASE NOTE: JDK6 is formerly known as Project Mustang