Swing on OSX: How to Trap command-Q?

Solution 1:

You can implement com.apple.eawt.ApplicationListener and respond to the Quit event. An example may be found in the Mac OS X Reference Library example, OSXAdapter.

Addendum: See Java for Mac OS X 10.6 Update 3 and 10.5 Update 8 Release Notes for information on deprecation, the redesigned com.apple.eawt.Application class, and the location of API documentation for the Apple Java extensions. Control-click or right-click on the .jdk file to Show Package Contents. You can browse the classes of com.apple.eawt among the OpenJDK sources.

As shown in this complete example, you can specify the desired QuitStrategy; a WindowListener will respond to ⌘Q:

Application.getApplication().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);

As noted here, you can set the property from the command line

java -Dapple.eawt.quitStrategy=CLOSE_ALL_WINDOWS -cp build/classes gui.QuitStrategyTest

or early in the program, before posting any GUI events:

System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
EventQueue.invokeLater(new QuitStrategyTest()::display);

image

Console, after ⌘Q:

java.vendor: Oracle Corporation
java.version: 1.8.0_60
os.name: Mac OS X
os.version: 10.11
apple.eawt.quitStrategy: CLOSE_ALL_WINDOWS
java.awt.event.WindowEvent[WINDOW_CLOSING,opposite=null,oldState=0,newState=0] on frame0

Code:

package gui;

import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JTextArea;

/**
 * @see https://stackoverflow.com/a/7457102/230513
 */
public class QuitStrategyTest {

    private void display() {
        JFrame f = new JFrame("QuitStrategyTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                System.out.println(e);
            }
        });
        f.add(new JTextArea(getInfo()));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private String getInfo() {
        String[] props = {
            "java.vendor",
            "java.version",
            "os.name",
            "os.version",
            "apple.eawt.quitStrategy"
        };
        StringBuilder sb = new StringBuilder();
        for (String prop : props) {
            sb.append(prop);
            sb.append(": ");
            sb.append(System.getProperty(prop));
            sb.append(System.getProperty("line.separator"));
        }
        System.out.print(sb);
        return sb.toString();
    }

    public static void main(String[] args) {
        System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
        EventQueue.invokeLater(new QuitStrategyTest()::display);
    }
}

Solution 2:

The top voted answer is excellent but just to fill in the "best way":

System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");

This will trigger the standard window closing callback event which should work really nicely for portable code.

As a result of the discussion below it seems that its crucial to do this really early in the app. I wrote this early in the static initializer of the main class before any UI code was executed.

Solution 3:

This is a pretty good question, and I must admit I don't have the answer. However, a couple years ago when I was working on a Java app and faced this problem, I solved it by registering a shutdown hook with the runtime that would do what I wanted the app to do before quitting. It's a heavy-handed solution but it worked. You can take a look at my code and see if it helps.