How to handle file open requests on MacOS

Danno Ferrin danno.ferrin at oracle.com
Mon Jan 5 17:23:27 UTC 2015


On Jan 5, 2015, at 10:07 AM, Mike Hearn <mike at plan99.net> wrote:

> 8u40 has javapackager improvements to help you do file associations in your
> installers. I'm looking forward to this, though for now I have to do it
> manually with 8u20.
> 
> But one problem I've found is that just setting up the file associations is
> not enough. You must handle the "openFile" messages sent by the OS as well.
> On Linux/Windows the interface is simple: the OS starts another copy of
> your app with the command line parameter. On MacOS you get a message posted
> to your message queue because apps are single instance by default. To
> handle this in JFX we must use internal APIs, like in the code snippet
> below.
> 
> Hopefully 8u40 will contain a proper cross platform file open request API
> that exposes this in a better way, otherwise the new javapackager support
> will be rather hard to use.

8u40 will not contain such an API, what is in 8u40 is what is visible in the EA releases.  Only major bugs will be fixed at this point.

For a demo proof of concept application I settled on a "two main" solution.  One main is for the Mac and one is for Win/Linux.  The win/linux main was the principal main class:

(warning: the demo was swing based, avert your eyes)

public class MainApplication {
    
/* <Snip> */

    void loadFile(File f) {
/* <Snip applicaiton logic> */    
    }

/* <More Snip> */

    public static void main(String [] args) {
        SwingUtilities.invokeLater(() -> { // My Eyes!  The Goggles, they do nothing!
            MainApplication app = new MainApplication();
            app.createGUI();
            if (args.length > 0) {
                File f = new File(args[0]);
                if (f.isFile()) {
                    app.loadFile(f);
                }
            }
        });
    }
}

The gist is that the main delegates to a load file method in the normal main, and the app has a stable state outside of the loaded file.

For Mac I wrote a MainApplicationMac that did mac-isms (not masochisms) in a different main method.  It extended strictly for method accessibility.

public class MainApplicationMac extends MainApplication {
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            MainApplication app = new MainApplication();
            app.createGUI();
            if (args.length > 0) {
                File f = new File(args[0]);
                if (f.isFile()) {
                    app.loadFile(f);
                }
            }
            
            Application.getApplication().setOpenFileHandler((AppEvent.OpenFilesEvent ofe) -> {
                List<File> files = ofe.getFiles();
                if (files != null && files.size() > 0) {
                    app.loadFile(files.get(0));
                }
            });
            
        });
    }
}

Really the only new code is the second block.

Also since I segregated out the mac stuff I did build gymnastics to ensure this is only built and packaged on a mac, so the introspection isn't done.  But I did have to add "-XDignore.symbol.file=true" to the javac arguments since the mac specific classes are not added to the symbol file for some odd reason.

Another thing you will notice that is different from mac and win/linux is that the same application receives all file open requests, whereas in win/linux you will see a new application opened for each request.


More information about the openjfx-dev mailing list