Becoming a Jedi, err...JNI Master!

I myself am far from being a JNI master, since I only started doing my first JNI a few days ago. The problem we were having is that there is no way in Java to get some native behavior on OS X. For example, the closest to native you can get with Open/Save file dialogs is what you get from java.awt.FileDialog, which isn't very much.

Since we're developing in Swing it is key that if we do anything, it should be wrapped up in a neat little package that mimics existing Swing components. Not only that, but we want the behavior of any wrapper classes to be the exact same, or very similar to, the corresponding JComponent. Although it's far from being ready for public release, I currently have a pretty solid JNI library for the Open/Save dialogs in OS X. It's all wrapped up neatly in CarbonFileChooser, an extension to JFileChooser.

One issue that arose, at least for OS X, is that there are various threads that have to run independently: the AWT/Swing thread, and the AppKit thread. Hence, when one makes a native call that is going to be doing some GUI stuff or event-oriented callbacks to the AWT/Swing thread, one has to forward things along to the AppKit thread. This can be done via the performSelectorOnMainThread method. To achieve modality, I use the following code:

public int showSaveDialog(Component parent) {
dialogOpen = true;
result = CANCEL_OPTION;

cc_showSaveDialog(parent);
while(dialogOpen) {
try {
Thread.sleep(100);
} catch(InterruptedException e) { }
}

return result;
}

What happens is that when the dialog is disposed, JNI calls are executed to set the dialogOpen variable to false, breaking the loop. I've been debating trying out wait() and using JNI to wake up the object instead, but for now the above code gets the job done.

Eventually I'm going to release this code library to the public so that anyone can use it, and eventually I'll open-source it (when I'm too lazy to keep maintaining it). The beauty of this, if one is using Swing, is that you really don't change your code at all. Just create the CarbonFileChooser class and you're good to go. Right now things are simple, so just some basic things can be done. You can do the following:
  • Set the initial directory
  • Add choosable filters, set the initial file filter, and get the chosen file filter once the dialog is disposed
  • Get the selected file to open/save
And some things left to do:
  • Multiple file selection and retrieval
  • Show no filters if the "All Files" filter is the only one
  • Setting the initial file name for save dialogs
  • Update dialog to unselect files, as necessary, when filter changes

2 comments:

Greg Kellum said...

Hi. I'm working on the same problem at the moment, and I've been having some difficulties most likely because I wasn't calling performSelectorOnMainThread. Could I have a look at your work?

Greg

Jason G said...

Hi Greg,

Sorry for the delay in responding. I'm not ready to throw my code out there yet, but I'd be willing to help you out with something more specific.

Not calling performSelectorOnMainThread will be problematic, mainly because the JNI code will be executed in a Java-based thread and not the AppKit thread, where it needs to be.