A while back I started a thread discussing how to neatly close a Swing app using both the standard window "X" button and a custom action such as a File menu with an Exit menu item. Ultimately, I came to the conclusion that the cleanest solution in many cases is to use a default close operation of JFrame.EXIT_ON_CLOSE and in any custom actions manually fire a WindowEvent with WindowEvent.WINDOW_CLOSING. Using this strategy, both the "X" button and the custom action act in the same manner and can be successfully intercepted by listening for a window closing event if any cleanup is required; furthermore, the cleanup could use dialogs to prompt for user actions without any ill effects.
I did, however, encounter one oddity that I mentioned in the previous thread. A dialog launched through SwingUtilities.invokeLater in the cleanup method would cause the app to not shutdown. This is somewhat of an academic curiosity as I am not sure you would ever have a rational need to do this, but I thought it would be interesting to explore more fully. Here is a complete example that demonstrates; see what you think:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CloseByWindowClosingTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
launchGUI();
}
});
}
private static void launchGUI() {
final JFrame frame = new JFrame("Close By Window Closing Test");
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JButton button1 = new JButton("No Dialog Close");
JButton button2 = new JButton("Dialog On Close");
JButton button3 = new JButton("Invoke Later Dialog On Close");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
postWindowClosingEvent(frame);
}
});
button2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "Test Dialog");
postWindowClosingEvent(frame);
}
});
button3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(frame, "Test Dialog");
}
});
postWindowClosingEvent(frame);
}
});
panel.add(button1);
panel.add(button2);
panel.add(button3);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
System.out.println("Received Window Closing Event");
}
});
frame.setVisible(true);
}
private static void postWindowClosingEvent(JFrame frame) {
WindowEvent windowClosingEvent = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
frame.getToolkit().getSystemEventQueue().postEvent(windowClosingEvent);
}
}
An additional note not in the example -- if in the button 3 scenario you were to put the window closing event posting inside the invoke later, the app then again closes. However, as implemented, what is it that causes button 3 to not result in the application closing?
Edited by: Skotty on Aug 11, 2009 5:08 PM -- Modified example code -- added the WindowAdapter to the frame as a window listener to show which buttons cause listeners to receive the window closing event.