Trapping Cmd-Q in a Java Swing application

Discussion in 'Web Design and Development' started by jerwin, Aug 10, 2016.

  1. jerwin macrumors 65816

    Joined:
    Jun 13, 2015
    #1
    A couple of years ago, when java was still a thing, I wrote myself a small diagram editing program in Swing.

    I still use it, but I'm trying to transition to various other codebases.

    When I close the window, or select the window's Quit Menu item, it's supposed to ask me to confirm.



    Screen Shot 1.png

    When I press cmd-quit, or select quit from the macintosh menu bar, it immediately terminates, and periodically, I lose what I've been working on that way. Is there a way to trap cmd-q?
     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
  3. jerwin thread starter macrumors 65816

    Joined:
    Jun 13, 2015
    #3
    I added this bit of code:

    Code:
    String osName = System.getProperty("os.name");
    
                if (osName.contains ("Mac"))
                {
                    Application macApp = Application.getApplication ();
                    com.apple.eawt.Application appleApplication = com.apple.eawt.Application.getApplication();
                    appleApplication.disableSuddenTermination();
                    appleApplication.setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
                    macApp.setQuitHandler (new QuitHandler ()
                    {
    
                        public void handleQuitRequestWith(QuitEvent e,
    
                                QuitResponse response)
    
                        {
                            response.performQuit();
                        }
    
                    });
    
                }
    
    Works for now, though it's probably fragile.

    Thanks.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    I'm not sure it will run on other platforms as-is. You can test it to find out, if it matters to you.

    You check for OS name, but you then follow that with a static reference to a class (com.apple.eawt.Application) that doesn't exist on other platforms. So when the classloader runs, and before your OS-check runs, the classloader will fail to find a
    com.apple.eawt.Application, and it won't run.

    To solve that, you have to reference all the platform-specific classes indirectly, i.e. by string name, using reflection. This means the classloader just sees a bunch of strings, rather than a compiled-in class reference, so the classloader will work. Then later when the code actually runs, if the OS isn't a Mac, none of the code does anything, so that's still safe.

    Yes, this is a big pain, but this is how you have to deal with classes that are platform-specific.

    There may be other ways of dealing with this, but this is one I know works, because I used to do it when I was writing cross-platform Java that had to use Mac-specific classes. It's not a big deal once you get the hang of it, and you'll learn about reflection in the process, so it's worthwhile.
     
  5. jerwin thread starter macrumors 65816

    Joined:
    Jun 13, 2015
    #5
    Reflection-- ok, I think I remember writing code that implemented a object customizer using reflection. A little depressing to realize that I've forgotten so much java.

    Time to fix these bugs!
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    I looked around a bit for my past projects where I did this, but didn't find any examples. That means I'd have to search old CDs, or maybe old HDs. That moves it way down on my list of Getting Around To It.


    As I recall, the simplest solution I used was to put all the handler code into a single class. That class is free to make static (compiled-in) references to any and all com.apple.eawt classes, methods, etc. It can also reference any classes specific to your application.

    Next, in the platform-checking part of your code, you reference that one class by reflection. IIRC, Class.forName(String) will suffice.

    Finally, you have a couple options about how to run the "attach the handlers" code.

    One way is to put it in a default constructor for the class. This makes it instantiable with newInstance(). Thus, the result of instantiating the class is to attach all the handlers. Because the class is referenced by name, the classloader won't try loading the Apple classes until you actually execute Class.forName().

    Another way is to put the "attach the handlers" code in a static initializer within the loaded class. When a class is loaded, all its static initializers are run (Java's rules of class-loading). Thus, the first Class.forName() will trigger the attachment of handlers, and no newInstance() is needed.

    There are other ways of doing this, including running methods via reflection, but that can be more of a pain than if you can isolate the handler-attaching into a single place. Isolation isn't always practical, so you'll have to assess what you're doing and what the options are.
     

Share This Page