JDK-4368790 : JButton stays pressed when focus stolen
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.3.0,1.4.1,1.4.2
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS:
    generic,windows_98,windows_nt,windows_2000,windows_xp generic,windows_98,windows_nt,windows_2000,windows_xp
  • CPU: generic,x86
  • Submitted: 2000-09-06
  • Updated: 2006-03-01
  • Resolved: 2006-02-16
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 JDK 6
5.0u8Fixed 6 b72Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
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.
27-01-2006

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

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(); }
28-09-2004

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 ======================================================================
26-09-2003

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
14-01-2002