Hi all,
I'm quite new to Swing & Java and I'm seeing behaviour which I cannot explain when the enter key is held down repeatedly triggering a JButton (Windows L&F). There's obviously some gap in my understanding here and I would like to understand why this is happening. I've knocked up a demo to illustrate what I'm seeing below. I've got a function, which is called by a JButton. This function first calls JOptionPane.showInputDialog and then sleeps for 200ms. At the beginning and end of the function I print a line to the console to state that the function has entered and exited.
If I
hold the "Enter" key down then the button is triggered and the JOptionPane dialog's button is then triggered (closing the dialog). The button is then triggered again and so on. This happens repeatedly ad infinitum.I expected that the button press would be called from the EDT and would finish executing before being called again. Because of this I would expect the output from the lines printed to look like this:
Entered function
Leaving function
Entered function
Leaving function
Entered function
Leaving function
Entered function
Leaving function
Entered function
Leaving function
and never like this
Entered function
Entered function
Entered function
Leaving function
Leaving function
Leaving function
The latter is what I'm seeing. When I hold the "Enter" key down and then release it sometimes I even get multiple instances of the JOptionPane dialog open. Please try it with the code below - hold enter for a few seconds and then release it, if the timing is right then there will be a JOptionPane open. Move or close the JOptionPane and sometimes there is another behind it.
The button press function is being called reentrantly by the EDT. This is shown by the stacktrace below.
In an attempt to stop this, I then wrapped the function it with a synchronized(this) block. This made no difference at all. I presume this is because the function is being called reentrantly by a single thread (the EDT) and java synchronization allows a thread to acquire a lock that it already owns.
There's a stacktrace below which shows:
1. the buttonActionPerformed function being invoked on the EDT
2. JOptionPane showInputDialog being called
3. another event being processed on the EDT
4. buttonActionPerformed being invoked again.
Perhaps cached button action events are being dispatched when the JOptionPane.showInputDialog function executes?
I've tried this with java 6 updates 2, 3 & 13.
If anyone could point out the error of my ways it would be appreciated :) Surely there is a way to prevent this occuring?
at Demo.buttonActionPerformed(Demo.java:64)
at Demo.access$000(Demo.java:10)
at Demo$2.actionPerformed(Demo.java:36)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
<snip>
at java.awt.Dialog$1.run(Dialog.java:1039)
at java.awt.Dialog$3.run(Dialog.java:1091)
at java.security.AccessController.doPrivileged(Native Method)
at java.awt.Dialog.show(Dialog.java:1089)
at javax.swing.JOptionPane.showInputDialog(JOptionPane.java:566)
at javax.swing.JOptionPane.showInputDialog(JOptionPane.java:508)
at Demo.buttonActionPerformed(Demo.java:65)
at Demo.access$000(Demo.java:10)
at Demo$2.actionPerformed(Demo.java:36)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
<snip>
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
import com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Demo extends JDialog
{
public Demo()
{
super(new JFrame(), true);
initComponents();
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
button = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
button.setText("Hold down \"Enter\"");
button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
buttonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(button, javax.swing.GroupLayout.DEFAULT_SIZE, 136, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(button, javax.swing.GroupLayout.DEFAULT_SIZE, 70, Short.MAX_VALUE)
.addContainerGap())
);
pack();
}// </editor-fold>
private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
// synchronized(this)
// {
System.out.println("Entered function " + System.identityHashCode(this));
JOptionPane.showInputDialog(this, "foo message", "foo title",
JOptionPane.QUESTION_MESSAGE);
try
{
Thread.sleep(200);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Leaving function " + System.identityHashCode(this));
// }
}
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
UIManager.setLookAndFeel(new WindowsClassicLookAndFeel());
}
catch (UnsupportedLookAndFeelException ex)
{
ex.printStackTrace();
System.out.println("Need windows L&F");
System.exit(-1);
}
Demo dialog = new Demo();
dialog.setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton button;
// End of variables declaration
}