I can get my JNI class to only partially execute due to my unfamiliarity with Objective-C and JNF. What I've tried so far is as follows:
Here's Open.java, which an MCV for a more significant Java Swing app that deserves a better-looking better-featured open file dialog than unreasonable java.awt.FileDialog or the ugly and less-featured javax.swing.JFileChooser.
import java.awt.event.*;
import javax.swing.*;
public class Open extends JFrame {
public Open () {
getContentPane().add(new JButton(new AbstractAction() {
public void actionPerformed (ActionEvent e) {
try {
NativeOpenFileDialog.run();
} catch (Throwable ex) {
System.err.println(ex.getMessage());
}
}
}));
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() { new Open(); }
});
}
}
Here's NativeOpenFileDialog.java
import javax.swing.*;
public class NativeOpenFileDialog {
static {
System.loadLibrary("natopndlg");
};
public static native void run ();
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() { NativeOpenFileDialog.run(); }
});
}
}
Here's NativeOpenFileDialog.m
#import <Cocoa/Cocoa.h>
#include <jni.h>
#include "NativeOpenFileDialog.h"
JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:YES];
[panel setCanChooseDirectories:YES];
[panel setAllowsMultipleSelection:YES];
if ([panel runModal] != NSModalResponseOK)
NSLog(@"User did not press OK");
[pool release];
}
And here's the makefile (I'm using Xcode 15.2):
SDK = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
Open.class: Open.java NativeOpenFileDialog.java NativeOpenFileDialog.m
javac -h . NativeOpenFileDialog.java
javac Open.java
gcc -dynamiclib -o libnatopndlg.dylib -framework Cocoa -isysroot $(SDK) -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin -fobjc-exceptions -std=c99 NativeOpenFileDialog.m
And here's how I execute it after executing make.
java Open
Clicking the button raises the exception:
NSInternalInconsistencyException', reason: 'NSWindow should only be instantiated on the main thread!
Which is understandable knowing that the panel is supposed to be opened on the UI thread. I believe all I have to do is make sure the window created by NSOpenPanel is a child of the JFrame and opened on the UI thread. And I believe that at leasat means I would have to execute a Java function to get the NSWindow of the JFrame.
You can execute things on the UI thread using dispatch_async() and dispatch_sync(). NSObject also includes a method performSelectorOnMainThread:withObject:waitUntilDone: that will perform a selector on the main thread.
This is a gist using the Java objective-c bridge that does something similar to this:
https://gist.github.com/anonymous/3966989
For your specific problem you could wrap the method body in dispatch_async
JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
dispatch_async(dispatch_get_main_queue(), ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:YES];
[panel setCanChooseDirectories:YES];
[panel setAllowsMultipleSelection:YES];
if ([panel runModal] != NSModalResponseOK)
NSLog(@"User did not press OK");
[pool release];
});
}
Of course, this just opens the dialog and doesn't do anything with the file that the user selects, so you'll need to build on this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With