Becoming a Jedi, err...JNI Master!
I myself am far from being a master of the Java Native Interface (JNI), 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