Swing on Osx: How to Trap Command-Q

Swing on OSX: How to Trap command-Q?

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.

How to intercept Cmd+Q

Implement the delegate method applicationShouldTerminate of NSApplication and show a custom modal alert. Depending on the answer return NSTerminateNow, NSTerminateCancel or NSTerminateLater.

In case of NSTerminateLater you can later call [NSApp replyToApplicationShouldTerminate:YES]; to finally quit the app.

Implement macOS hide on quit behavior with JFrame/Swing

Oh, I forgot to really specify what the behavior I'm trying to mimic really was. When you press the quit button on macOS, the window is made invisible. When you click the app's icon in the dock the window should be made visible again.

You really should read the JavaDocs, that and little bit of Googling brought me around to this simple example...

import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.desktop.AppReopenedEvent;
import java.awt.desktop.AppReopenedListener;
import java.awt.desktop.QuitStrategy;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Test {

public static void main(String[] args) {
new Test();

Desktop.getDesktop().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
}

public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Desktop.getDesktop().addAppEventListener(new AppReopenedListener() {
@Override
public void appReopened(AppReopenedEvent e) {
frame.setVisible(true);
}

});
}
});
}

public class TestPane extends JPanel {

public TestPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new GridBagLayout());
add(new JLabel("Now you see me"));
}

}
}

JFrame DO_NOTHING_ON_CLOSE is not working on Command + Q

Apple provides a couple of useful APIs. Start by having a look at Java for OS X v10.6 Update 3 and 10.5 Update 8 Release Notes and this example for more details.

In particular, what you're really interested in

Sudden Termination

Java applications on OS X v10.6 can now opt-in to being suddenly terminated by the OS to speed log-out and shut down.
Apps can raise and lower their sudden termination count with the
enableSuddenTermination() and disableSuddenTermination() methods on
the com.apple.eawt.Application class. More information on Sudden
Termination is available in NSProcessInfo Class Reference. (5756768)

Default Quit Action

Applications can now request that the eAWT send WindowClosing events to all open windows instead of calling
System.exit(0) when the user choose Quit from the application menu. By
setting the apple.eawt.quitStrategy system property to
CLOSE_ALL_WINDOWS, the eAWT will send a close event to every window in
back-to-front order (3198576).

This should allow you to better control how the app is terminated, for example...

import com.apple.eawt.Application;
import com.apple.eawt.FullScreenUtilities;
import com.apple.eawt.QuitStrategy;
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;

public class Test {

public static void main(String[] args) {
Application.getApplication().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
Application.getApplication().disableSuddenTermination();
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setUndecorated(true);
FullScreenUtilities.setWindowCanFullScreen(frame, true);
frame.setLayout(new GridBagLayout());
JButton close = new JButton("Close me");
close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
frame.dispose();
}
});
frame.add(close);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
System.out.println("Closed");
}

@Override
public void windowClosing(WindowEvent e) {
System.out.println("Closing");
}

});
frame.setLocationRelativeTo(null);
//Application.getApplication().requestToggleFullScreen(frame);
//frame.setVisible(true);
GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.setFullScreenWindow(frame);
}
});
}
}

When pressing CMD+Q, the windowClosing event is triggered, meaning the only way to terminate the app is tap the close button

Java 9

It should be noted that, for those people lucky enough to be using it, Java 9 adds much of the support which was previously found in com.apple.eawt to the API via the java.awt.Desktop API

Java Swing Mac OSX Preferences Menu

You might be able to use OSXAdapter, which "uses a reflection proxy model to hook existing preferences, about, and quit functionality." See also this answer.

How to disable Mac's About/Quit menu items when showing a JDialog?

By default, the application menu entries do nothing when a modal dialog has focus, but the appearance is unaffected; this is standard on Mac OS X interface. Of course, your application's own menus should be enabled or disabled as appropriate. You can intercept the relevant events using OSXAdapter, as shown in this answer.

processWindowEvent in mac

A world without com.apple.eawt.*

You need to look towards java.awt.Desktop instead.

For example...

Desktop.getDesktop().setQuitHandler(new QuitHandler() {
@Override
public void handleQuitRequestWith(QuitEvent e, QuitResponse response) {
// Do some stuff
//response.cancelQuit();
//response.performQuit();
}
});
Desktop.getDesktop().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);

Original Answer

Welcome to the wonderful world of "Apple does everything differently"

Basically what's happening is, when you "Quit" the program, Apple is calling System.exit(0), basically the same thing that would occur if your used CMD+Q

Now, Apple provides an API which provides functionality which you can use to "configure" your App with MacOS and perform some functionality which is unique to Apple, the problem is, it's a complete pain in the ... code to find useful information about and use.

What you're looking for is com.apple.eawt.ApplictionsetQuitStrategy. This defaults to calling System.exit(0), but you can change it to "close all windows" instead.

In this case, it would allow you to trap the WindowEvent and do what ever it is you want to do

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test {

public static void main(String[] args) {
new Test();
}

public Test() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("Closing");
System.exit(0);
}
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

try {
Class quitStrategies = Class.forName("com.apple.eawt.QuitStrategy");
Object quitStrategy = null;
for (Object o : quitStrategies.getEnumConstants()) {
if ("CLOSE_ALL_WINDOWS".equals(o.toString())) {
quitStrategy = o;
}
}
if (quitStrategy != null) {
Class appClass = Class.forName("com.apple.eawt.Application");
Class params[] = new Class[]{};

Method getApplication = appClass.getMethod("getApplication", params);
Object application = getApplication.invoke(appClass);
Method setQuitStrategy = application.getClass().getMethod("setQuitStrategy", quitStrategies);
setQuitStrategy.invoke(application, quitStrategy);
}

} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException ex) {
ex.printStackTrace();
}
}
});
}

public class TestPane extends JPanel {
}
}

My general advice is, build a nice "Mac" utilities class which encapsulates the functionality you want to play with and call that.

Also beware, this functionality may disappear suddenly in future releases.

It should be noted that if you intend to have a "one for all" application, you will need to use reflection, as the required API is not available in the standard API, but if you wanted to make a "Apple" only release, you should have a look at this for more information about how you can compile the code on MacOS, because using...

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

is hell of a lot easier to write and understand

how can I develop Apple Java Extensions on Windows?

I created an updated jar to solve this problem. GitHub source here.
It's hosted on maven central



Related Topics



Leave a reply



Submit