23

After being convinced ("schooled") that Swing apps on Mac do look native, I'm trying to make mine look as native as possible. Everything looks great, but when I hit command+Q or do it from the menu, my windowStateChanged(WindowEvent e) is not firing on my main JFrame (if I exit in any other way, it does fire). How can I respond to the real Apple quit?

Community
  • 1
  • 1
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421

6 Answers6

16

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);
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Excellent, I found that slightly after, but what do I do if I don't have the com.apple.eawt package? Or is it available on Windows too? – Dan Rosenstark Jan 14 '10 at 00:27
  • 1
    Awesome, it's all clear now. It calls all the classes dynamically. My code ended up being one line (plus Apple's class): OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod("onClose", (Class[])null)); – Dan Rosenstark Jan 14 '10 at 01:02
  • You have it exactly; I added some additional links to Apple's example for future reference. – trashgod Jan 14 '10 at 02:48
  • 2
    It seems that since release 1.6 the class is deprecated. See: https://developer.apple.com/library/mac/#releasenotes/Java/JavaSnowLeopardUpdate3LeopardUpdate8RN/NewandNoteworthy/NewandNoteworthy.html Redesigned eAWT –  Feb 26 '12 at 22:13
7

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.

Community
  • 1
  • 1
Shai Almog
  • 51,749
  • 5
  • 35
  • 65
  • isn't this the same as `Application.getApplication().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);`? – Dan Rosenstark Oct 12 '15 at 22:21
  • 3
    Yep. But it doesn't require access to the Application class which you wouldn't want for a portable app. Its one line of code instead of reflection. – Shai Almog Oct 13 '15 at 04:16
  • 1
    @Yar: Sadly, they're _not_ the same with regard to `command-Q`; I've updated the [example](http://stackoverflow.com/a/30308671/230513) cited [here](http://stackoverflow.com/a/2061318/230513) to show a portable approach; elide the call to `enableOSXQuitStrategy()` to se the effect. – trashgod Oct 15 '15 at 02:04
  • Damn you are correct. This just doesn't seem to be doable in a portable way without reflection! Thanks for the code! – Shai Almog Oct 15 '15 at 03:19
  • Actually no. My test case was flawed. This worked for me both with the cmd-Q and the quit menu option. On which version of the JDK does cmd-Q not work? – Shai Almog Oct 15 '15 at 03:54
  • @ShaiAlmog: I tested various combinations of Java 1.8.0_[45,60] on Mac OS X 10.[9,10,11]; your approach works as documented to send `WINDOW_CLOSING` when clicking the red close button but not via `command-Q` or the applicaiotn menu; I'd be happy to find I'm wrong! :-) – trashgod Oct 15 '15 at 11:34
  • Odd, that's the exact version of Java I'm using too. I'm still on Yosemite maybe the OS version has something to do with that? – Shai Almog Oct 15 '15 at 12:14
  • That explains it. I set it in my static initializer code before anything else. – Shai Almog Oct 17 '15 at 14:41
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.

mipadi
  • 398,885
  • 90
  • 523
  • 479
3

I was originally seeing a 'access restriction' violation when trying to access the com.apple.eawt.Application and com.apple.eawt.* subclasses.

(Note: I'm programming on a MAC, using Eclipse, with Java 1.6 using Swing)

So I needed to modify my java build path to allow access to the apple subclasses by adding "com/apple/eawt/**" access rule. After that this code below was able to compile and work for me:

//NOTE: This code only works for MAC OS.  If you run this on Windows
//the application never starts (so you literally need to remove this block of code)

import com.apple.eawt.*;
import com.apple.eawt.QuitHandler;

 Application a = Application.getApplication();
 a.setQuitHandler(new QuitHandler() {


@Override
public void handleQuitRequestWith(com.apple.eawt.AppEvent.QuitEvent qe, com.apple.eawt.QuitResponse qr) {
    // TODO Auto-generated method stub

    int res = JOptionPane.showConfirmDialog(frame, "Are you sure you want to exit the program?", "Quit ?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

    if (res == JOptionPane.YES_OPTION) 
        qr.performQuit();
    else
        qr.cancelQuit();

   }

});
ShepDog
  • 31
  • 1
2

Have you tried setting up command-Q as an accelerator in your menu? Can you make your app respond to it?

I'm not positive, but I think this works in Linux and probably Windows with the equivalent Alt-F4. My app responds to the "killing" keystroke, I process some cleanup code and then I do a programmatic System.exit().

If you're "just" after graceful exit handling, you may also want to catch the WindowEvent WINDOW_CLOSING, where traditionally "are you sure?" stuff gets done.

Andrew
  • 101
  • 1
  • 10
Carl Smotricz
  • 66,391
  • 18
  • 125
  • 167
  • won't work, because you can trip the menu item with your mouse too. – Dan Rosenstark Jan 13 '10 at 23:51
  • 1
    So what's wrong with that? It makes sense for your app to respond to the keystroke as well as the "Quit" menu item, and identically so. – Carl Smotricz Jan 13 '10 at 23:53
  • Thanks for this first round. I do catch the WindowEvent. Works if I press the little red dot to close, but NOT if I use the "Quit MyApp cmd-Q"... – Dan Rosenstark Jan 13 '10 at 23:54
  • Sorry, I'm not being clear. On OSX, one doesn't put the Quit menu item under file. It's part of the OS. All apps have it, like it or not. – Dan Rosenstark Jan 13 '10 at 23:55
  • 1
    Sure. The red dot closes the window but not the app - that's the Apple way. I really suggest you try putting cmd-Q in your menu before rejecting my answer out of hand. – Carl Smotricz Jan 13 '10 at 23:57
  • Okay, you're right. But also I just found this "Apple provides functionality for this in the com.apple.eawt.* Java classes. The Application and ApplicationAdaptor classes provide a way to handle the Preferences, About, and Quit items."... don't know if that's right either. Easier to try your suggestion first. – Dan Rosenstark Jan 13 '10 at 23:58
  • 1
    Great, and I hope you get it working. But I do have to face the fact that I have too little Apple-specific Java experience to help you any further, so I'm going to throw in the towel with what grace I can muster. Good luck! – Carl Smotricz Jan 14 '10 at 00:08
  • Cool, I'll upvote you so you'll have to live with this answer forever :) – Dan Rosenstark Jan 14 '10 at 00:26
  • I would comment on the QUESTION above, but then you'd never see it. Just wanted to respond to your comment in the Q and say that, "yes, it's true, Apple does kind of need their own Java implementation" because they're so heavy-handed with standard app behavior. Probably same fascism as runs the Apple Store. Not sure if it's morally okay, but the results are pretty interesting (usability of OS-wise). Anyway, I can see this is a pro-Mac comment, so I'll drop it before it offends.... – Dan Rosenstark Jan 15 '10 at 00:33
  • 2
    Thank you, that's thoughtful. My answer is of little worth but I'm leaving it up to keep the discussion visible. Yes, Apple bullies the developer but the result is a very smooth user experience. Pros and cons to everything, I guess. I'm not sure it's the same in the Apple Store; as I understand it, much of what they do there is for the bottom line, not user convenience. – Carl Smotricz Jan 15 '10 at 08:02
0

Looking at the link to Java for Mac OS X 10.6 Update 3 and 10.5 Update 8 Release Notes I noticed that there is a section on Default Quit Action. This describes a system property to request that all windows are closed in response to the "Quit" menu item, which sounds like exactly what is needed? I have used this in my own application (using Info.plist to set the property on OS X only), and it seems to work as described. This would presumably only work on recent Java/OS X versions, but for those platforms seems like a neat solution, and doesn't require any code changes.