United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 4368790 JButton stays pressed when focus stolen
4368790 : JButton stays pressed when focus stolen

Details
Type:
Bug
Submit Date:
2000-09-06
Status:
Resolved
Updated Date:
2006-03-01
Project Name:
JDK
Resolved Date:
2006-02-16
Component:
client-libs
OS:
windows_nt,generic,windows_98,windows_xp,windows_2000
Sub-Component:
javax.swing
CPU:
x86,generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.3.0,1.4.1,1.4.2
Fixed Versions:
6

Related Reports
Backport:
Duplicate:
Duplicate:
Duplicate:
Duplicate:
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description
Name: skT45625			Date: 09/06/2000


java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build
Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

A user enters a value in to a text field and then trys to click an apply
button.  The focus listener on the text field detects that the focus is being
lost so it post-validates the text field value.  It finds that an illegal value
has been entered so it 1. requests that the focus be returned to it and 2.
displays a JOptionPane error message.  Strange things happen with the state of
the JButton that was pressed.

Two examples:

1. IntField2 example - Type in a value other than "blah" into the text field
and then click a button.  You will get the illegal value dialog and focus will
be returned to the text field.  After you do this a couple of times (usually on
the second try), you will notice that when you move your mouse over the button
it appears pressed and when your mouse is not over the button, it is not
pressed.  If you use this example with 1.2.2 instead of 1.3, the button stays
pressed all the time.

2. VerifierTest example - a modified version of a demo written by Sun to
demonstrate the InputVerifier class.  Type in a value other than "pass" into
the top field then click on the button.  You will notice the error dialog pops
up and then all appears fine.  However, if you resize the frame, the button
will redraw and you will see that it is in the pressed state.  After you mouse
over it for the first time, it will then show as pressed when the mouse is over
it.


// EXAMPLE 1 *******************************************

import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.text.*;


public class IntField2 {
    
    public static void main(String[] args) {
        JFrame f = new JFrame("IntField2 Demo");
        Container p = f.getContentPane();
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));

        final JTextField tf = new JTextField("blah");

        tf.addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent e) {
                System.out.println("Trying to take focus");
                if (!tf.getText().equals("blah")) {
                    
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            tf.requestFocus();
                            
                            String message = "illegal value: " + tf.getText();
                            JOptionPane.showMessageDialog(tf.getParent(),
message,
                                                    "Illegal Value",
JOptionPane.ERROR_MESSAGE);
                                                    
                            
                        }
                    });
                    tf.setText("blah");
                    
                }

            }
        });

        final JButton b = new JButton("Button 1");
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (b.hasFocus()) {
                    System.out.println("Button clicked." + b.isSelected() +
tf.getText());
                    //b.setBorder(RAISE);
                    System.out.print(b.getBackground());
                }
                    
                else
                    System.out.println("(didn't have focus)" + b.isSelected() +
tf.getText());
            }

        });

        final JButton c = new JButton("Button 2");
        c.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("trying to reset b");
                b.repaint();
                b.invalidate();
                b.revalidate();
                b.requestFocus();
                
            }
        });
        
        
//        p.add(a);
        p.add(b);
        p.add(c);
        p.add(tf);
        
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        
        f.pack();
        f.setVisible(true);
    }
}


// EXAMPLE 2 ****************************************

 import java.awt.*;
 import java.util.*;
 import java.awt.event.*;
 import javax.swing.*;
 
 // This program demonstrates the use of the Swing InputVerifier class.
 // It creates two text fields; the first of the text fields expects the
 // string "pass" as input, and will allow focus to advance out of it
 // only after that string is typed in by the user.

 class VerifierTest extends JFrame {

  public VerifierTest () {
    JTextField tf;
    tf = new JTextField ("TextField1");
			   
    getContentPane().add (tf, BorderLayout.NORTH);
    tf.setInputVerifier(new PassVerifier());

    tf = new JTextField ("TextField2");
    getContentPane().add (tf, BorderLayout.SOUTH);
    
    final JButton b = new JButton("Button");
    b.setVerifyInputWhenFocusTarget(true);
    getContentPane().add (b, BorderLayout.EAST);
    b.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            if (b.hasFocus())
                System.out.println("Button clicked");
        }
    });
                  
    addWindowListener (new MyWAdapter ());
  }



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

   class MyWAdapter extends WindowAdapter {

     public void windowClosing(WindowEvent event) {
       System.exit (0);
     }
   }
               
   class PassVerifier extends InputVerifier {

     public boolean verify(JComponent input) {
       JTextField tf = (JTextField) input;
       String pass = tf.getText();
       if (pass.equals("pass")) return true;
       else {
             String message = "illegal value: " + tf.getText();
             JOptionPane.showMessageDialog(tf.getParent(), message,
                              "Illegal Value", JOptionPane.ERROR_MESSAGE);
       
         return false;
       }
     }
   }
 }
(Review ID: 109338) 
======================================================================

                                    

Comments
EVALUATION

Adding setPressed(false) to BasicButtonListener as described fixes the first case. That is what I've chosen to do to resolve this bug. The more complicated problem described in the second bug will be handled by 4533820.
                                     
2006-01-27
EVALUATION

The fix to this bug also needs to address duplicate bug 6369054 which is nearly the same problem but slightly different.
                                     
2006-01-09
SUGGESTED FIX

------- BasicButtonListener.java -------
*** //C/tmp/sccs.001000	Fri Jan 27 16:11:01 2006
--- BasicButtonListener.java	Fri Jan 27 16:05:47 2006
***************
*** 177,183 ****
  	   }
  	}
  
!         b.getModel().setArmed(false);
  
  	b.repaint();
      }
--- 177,185 ----
  	   }
  	}
  
!         ButtonModel model = b.getModel();
!         model.setArmed(false);
!         model.setPressed(false);
  
  	b.repaint();
      }
                                     
2004-09-28
EVALUATION

What is happening here is that the button is never receiving the MouseReleased event, and therefore gets left in a wierd state. When a modal dialog is created before the mouse is released, as this example does, the resulting released event is getting lost.
Unfortunately there is no way to ensure Swing gets the mouse released, and thus we are left in this state. Either we need to guarantee the component that got the pressed will get the released, or we need additional API to make sure there is access to the released.
scott.violet@eng 2000-10-27


I'm reassigning this to AWT for further investigation if there is anything that can be done to enable a component that gets a pressed to get the corresponding release.
scott.violet@eng 2001-03-15

Sounds like a focus issue.  I'll let Hania decide what timeframe this 
should be addressed in (if the focus rearchitecture hasn't done it already). 
eric.hawkes@eng 2001-03-19

I don't see how this is a focus issue. Reassigning to Brent since it sounds like a mouse events delivery issues.

hania.gajewska@Eng 2001-04-17

This is reproducible relatively easily using SwingSet2 w/ merlin beta refresh.  On the Dialog tab, click rapidly on the buttons to show the dialogs.  I was able to reproduce the problem in just a few tries.
brent.christian@eng 2001-05-29

This can also be reproduced on Solaris using only the keyboard (and a point-to-focus window manager).  Press and hold the space bar to create a Dialog (several may appear).  As long as the Dialog is activated (i.e. the mouse cursor points in it), the button will stay "stuck" down.  Presumably, this is because the key_released event is never received by the JButton.
###@###.### 2001-12-06

After quite a bit of investigation, I've discovered a couple of strategies that could be used to fix this bug.  One would involve modifying the code which delivers MOUSE_RELEASED events.  This would likely touch code involving modal dialogs, as well as the lightweight dispatcher (the nitty-gritty of what currently happens to the MOUSE_RELEASED events in question is in the comments section).  A less far-reaching and much safer fix would be a simple change to the Basic L&F ButtonListener to reset the button's model to unpressed on a focusLost().  The Swing fix makes sense, because a button will NEVER be in a pressed state if it has lost focus.  I've tried the suggested fix, and it also fixes the keyboard problem on Solaris described above.  I'm bumping this over to Swing for the final decision.
###@###.### 2002-01-14

My initial testing of ###@###.###'s idea shows that it is an appropriate fix. However, the setPressed(false) needs to come after the setArmed(false) otherwise we will send an actionPerformed, which is incorrect.
###@###.### 2003-09-04

Name: ibR10256			Date: 09/26/2003


The suggested fix helps with the first sample but doesn't help with
the second, as here BasicButtonListener.focusLost() is not even
called after the button press because the button doesn't have the focus.
The focus is not requested when IntputVerifier.verify() returns false
(see JComponent.requestFocus()).

###@###.### 2003-09-26



======================================================================
                                     
2003-09-26
WORK AROUND

Name: skT45625			Date: 09/06/2000


none, I know of no way to change a normal JButton's pressed state without
changing the button model
======================================================================

By installing a Button L&F which reset's the button's model to be unpressed in response to a focusLost(), this problem may be worked around.
###@###.### 2002-01-14
                                     
2002-01-14



Hardware and Software, Engineered to Work Together