mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-27 03:46:17 -06:00
System File Chooser: support "approve" callback and system message dialog on Windows and Linux (not yet used in SystemFileChooser
This commit is contained in:
@@ -29,6 +29,9 @@
|
||||
// declare external methods
|
||||
extern Window getWindowHandle( JNIEnv* env, JAWT* awt, jobject window, Display** display_return );
|
||||
|
||||
// declare internal methods
|
||||
static jobjectArray fileListToStringArray( JNIEnv* env, GSList* fileList );
|
||||
|
||||
//---- class AutoReleaseStringUTF8 --------------------------------------------
|
||||
|
||||
class AutoReleaseStringUTF8 {
|
||||
@@ -142,10 +145,37 @@ static void handle_realize( GtkWidget* dialog, gpointer data ) {
|
||||
g_object_unref( gdkOwner );
|
||||
}
|
||||
|
||||
struct ResponseData {
|
||||
JNIEnv* env;
|
||||
jobject callback;
|
||||
GSList* fileList;
|
||||
|
||||
ResponseData( JNIEnv* _env, jobject _callback ) {
|
||||
env = _env;
|
||||
callback = _callback;
|
||||
fileList = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
static void handle_response( GtkWidget* dialog, gint responseId, gpointer data ) {
|
||||
// get filenames if user pressed OK
|
||||
if( responseId == GTK_RESPONSE_ACCEPT )
|
||||
*((GSList**)data) = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER( dialog ) );
|
||||
if( responseId == GTK_RESPONSE_ACCEPT ) {
|
||||
ResponseData *response = static_cast<ResponseData*>( data );
|
||||
if( response->callback != NULL ) {
|
||||
GSList* fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER( dialog ) );
|
||||
jobjectArray files = fileListToStringArray( response->env, fileList );
|
||||
|
||||
GtkWindow* window = GTK_WINDOW( dialog );
|
||||
|
||||
// invoke callback: boolean approve( String[] files, long hwnd );
|
||||
jclass cls = response->env->GetObjectClass( response->callback );
|
||||
jmethodID approveID = response->env->GetMethodID( cls, "approve", "([Ljava/lang/String;J)Z" );
|
||||
if( approveID != NULL && !response->env->CallBooleanMethod( response->callback, approveID, files, window ) )
|
||||
return; // keep dialog open
|
||||
}
|
||||
|
||||
response->fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER( dialog ) );
|
||||
}
|
||||
|
||||
// hide/destroy file dialog and quit loop
|
||||
gtk_widget_hide( dialog );
|
||||
@@ -159,7 +189,7 @@ extern "C"
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
|
||||
( JNIEnv* env, jclass cls, jobject owner, jboolean open,
|
||||
jstring title, jstring okButtonLabel, jstring currentName, jstring currentFolder,
|
||||
jint optionsSet, jint optionsClear, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
jint optionsSet, jint optionsClear, jobject callback, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
{
|
||||
// initialize GTK
|
||||
if( !gtk_init_check( NULL, NULL ) )
|
||||
@@ -222,8 +252,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrar
|
||||
|
||||
// show dialog
|
||||
// (similar to what's done in sun_awt_X11_GtkFileDialogPeer.c)
|
||||
GSList* fileList = NULL;
|
||||
g_signal_connect( dialog, "response", G_CALLBACK( handle_response ), &fileList );
|
||||
ResponseData responseData( env, callback );
|
||||
g_signal_connect( dialog, "response", G_CALLBACK( handle_response ), &responseData );
|
||||
gtk_widget_show( dialog );
|
||||
|
||||
// necessary to bring file dialog to the front (and make it active)
|
||||
@@ -241,10 +271,14 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrar
|
||||
gtk_main();
|
||||
|
||||
// canceled?
|
||||
if( fileList == NULL )
|
||||
if( responseData.fileList == NULL )
|
||||
return newJavaStringArray( env, 0 );
|
||||
|
||||
// convert GSList to Java string array
|
||||
return fileListToStringArray( env, responseData.fileList );
|
||||
}
|
||||
|
||||
static jobjectArray fileListToStringArray( JNIEnv* env, GSList* fileList ) {
|
||||
guint count = g_slist_length( fileList );
|
||||
jobjectArray array = newJavaStringArray( env, count );
|
||||
GSList* it = fileList;
|
||||
@@ -259,3 +293,52 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrar
|
||||
g_slist_free( fileList );
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showMessageDialog
|
||||
( JNIEnv* env, jclass cls, jlong hwndParent, jint messageType, jstring primaryText, jstring secondaryText,
|
||||
jint defaultButton, jobjectArray buttons )
|
||||
{
|
||||
GtkWindow* window = (GtkWindow*) hwndParent;
|
||||
|
||||
// convert message type
|
||||
GtkMessageType gmessageType;
|
||||
switch( messageType ) {
|
||||
case /* JOptionPane.ERROR_MESSAGE */ 0: gmessageType = GTK_MESSAGE_ERROR; break;
|
||||
case /* JOptionPane.INFORMATION_MESSAGE */ 1: gmessageType = GTK_MESSAGE_INFO; break;
|
||||
case /* JOptionPane.WARNING_MESSAGE */ 2: gmessageType = GTK_MESSAGE_WARNING; break;
|
||||
case /* JOptionPane.QUESTION_MESSAGE */ 3: gmessageType = GTK_MESSAGE_QUESTION; break;
|
||||
default:
|
||||
case /* JOptionPane.PLAIN_MESSAGE */ -1: gmessageType = GTK_MESSAGE_OTHER; break;
|
||||
}
|
||||
|
||||
// convert Java strings to C strings
|
||||
AutoReleaseStringUTF8 cprimaryText( env, primaryText );
|
||||
AutoReleaseStringUTF8 csecondaryText( env, secondaryText );
|
||||
|
||||
// create GTK file chooser dialog
|
||||
// https://docs.gtk.org/gtk3/class.MessageDialog.html
|
||||
jint buttonCount = env->GetArrayLength( buttons );
|
||||
GtkWidget* dialog = gtk_message_dialog_new( window, GTK_DIALOG_MODAL, gmessageType,
|
||||
(buttonCount > 0) ? GTK_BUTTONS_NONE : GTK_BUTTONS_OK,
|
||||
"%s", (const gchar*) cprimaryText );
|
||||
if( csecondaryText != NULL )
|
||||
gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( dialog ), "%s", (const gchar*) csecondaryText );
|
||||
|
||||
// add buttons
|
||||
for( int i = 0; i < buttonCount; i++ ) {
|
||||
AutoReleaseStringUTF8 str( env, (jstring) env->GetObjectArrayElement( buttons, i ) );
|
||||
gtk_dialog_add_button( GTK_DIALOG( dialog ), str, i );
|
||||
}
|
||||
|
||||
// set default button
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( dialog ), MIN( MAX( defaultButton, 0 ), buttonCount - 1 ) );
|
||||
|
||||
// show message dialog
|
||||
gint responseID = gtk_dialog_run( GTK_DIALOG( dialog ) );
|
||||
gtk_widget_destroy( dialog );
|
||||
|
||||
// return -1 if closed with ESC key
|
||||
return (responseID >= 0) ? responseID : -1;
|
||||
}
|
||||
|
||||
@@ -40,10 +40,18 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_xS
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary
|
||||
* Method: showFileChooser
|
||||
* Signature: (Ljava/awt/Window;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;III[Ljava/lang/String;)[Ljava/lang/String;
|
||||
* Signature: (Ljava/awt/Window;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeLinuxLibrary/FileChooserCallback;I[Ljava/lang/String;)[Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
|
||||
(JNIEnv *, jclass, jobject, jboolean, jstring, jstring, jstring, jstring, jint, jint, jint, jobjectArray);
|
||||
(JNIEnv *, jclass, jobject, jboolean, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary
|
||||
* Method: showMessageDialog
|
||||
* Signature: (JILjava/lang/String;Ljava/lang/String;I[Ljava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showMessageDialog
|
||||
(JNIEnv *, jclass, jlong, jint, jstring, jstring, jint, jobjectArray);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ tasks {
|
||||
compilerArgs.addAll( toolChain.map {
|
||||
when( it ) {
|
||||
is Gcc, is Clang -> listOf( "-O2", "-DUNICODE" )
|
||||
is VisualCpp -> listOf( "/O2", "/Zl", "/GS-", "/DUNICODE" )
|
||||
is VisualCpp -> listOf( "/O2", "/GS-", "/DUNICODE" )
|
||||
else -> emptyList()
|
||||
}
|
||||
} )
|
||||
@@ -81,7 +81,7 @@ tasks {
|
||||
linkerArgs.addAll( toolChain.map {
|
||||
when( it ) {
|
||||
is Gcc, is Clang -> listOf( "-lUser32", "-lGdi32", "-lshell32", "-lAdvAPI32", "-lKernel32", "-lDwmapi", "-lOle32", "-luuid" )
|
||||
is VisualCpp -> listOf( "User32.lib", "Gdi32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "Dwmapi.lib", "Ole32.lib", "uuid.lib", "/NODEFAULTLIB" )
|
||||
is VisualCpp -> listOf( "User32.lib", "Gdi32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "Dwmapi.lib", "Ole32.lib", "uuid.lib" )
|
||||
else -> emptyList()
|
||||
}
|
||||
} )
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
// declare external methods
|
||||
extern HWND getWindowHandle( JNIEnv* env, jobject window );
|
||||
|
||||
// declare internal methods
|
||||
static jobjectArray getFiles( JNIEnv* env, jboolean open, IFileDialog* dialog );
|
||||
|
||||
//---- class AutoReleasePtr ---------------------------------------------------
|
||||
|
||||
template<class T> class AutoReleasePtr {
|
||||
@@ -38,6 +41,10 @@ public:
|
||||
AutoReleasePtr() {
|
||||
ptr = NULL;
|
||||
}
|
||||
AutoReleasePtr( T* p ) {
|
||||
ptr = p;
|
||||
ptr->AddRef();
|
||||
}
|
||||
~AutoReleasePtr() {
|
||||
if( ptr != NULL )
|
||||
ptr->Release();
|
||||
@@ -126,6 +133,86 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//---- class DialogEventHandler -----------------------------------------------
|
||||
|
||||
// see https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appplatform/commonfiledialog/CommonFileDialogApp.cpp
|
||||
|
||||
class DialogEventHandler : public IFileDialogEvents {
|
||||
JNIEnv* env;
|
||||
jboolean open;
|
||||
jobject callback;
|
||||
LONG refCount = 1;
|
||||
|
||||
public:
|
||||
DialogEventHandler( JNIEnv* _env, jboolean _open, jobject _callback ) {
|
||||
env = _env;
|
||||
open = _open;
|
||||
callback = _callback;
|
||||
}
|
||||
|
||||
//---- IFileDialogEvents methods ----
|
||||
|
||||
IFACEMETHODIMP OnFileOk( IFileDialog* dialog ) {
|
||||
if( callback == NULL )
|
||||
return S_OK;
|
||||
|
||||
// get files
|
||||
jobjectArray files;
|
||||
if( open ) {
|
||||
AutoReleasePtr<IFileOpenDialog> openDialog;
|
||||
HRESULT hr = dialog->QueryInterface( &openDialog );
|
||||
files = SUCCEEDED( hr ) ? getFiles( env, true, openDialog ) : getFiles( env, false, dialog );
|
||||
} else
|
||||
files = getFiles( env, false, dialog );
|
||||
|
||||
// get hwnd of file dialog
|
||||
HWND hwndFileDialog = 0;
|
||||
AutoReleasePtr<IOleWindow> window;
|
||||
if( SUCCEEDED( dialog->QueryInterface( &window ) ) )
|
||||
window->GetWindow( &hwndFileDialog );
|
||||
|
||||
// invoke callback: boolean approve( String[] files, long hwnd );
|
||||
jclass cls = env->GetObjectClass( callback );
|
||||
jmethodID approveID = env->GetMethodID( cls, "approve", "([Ljava/lang/String;J)Z" );
|
||||
if( approveID == NULL )
|
||||
return S_OK;
|
||||
return env->CallBooleanMethod( callback, approveID, files, hwndFileDialog ) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OnFolderChange( IFileDialog* ) { return S_OK; }
|
||||
IFACEMETHODIMP OnFolderChanging( IFileDialog*, IShellItem* ) { return S_OK; }
|
||||
IFACEMETHODIMP OnHelp( IFileDialog* ) { return S_OK; }
|
||||
IFACEMETHODIMP OnSelectionChange( IFileDialog* ) { return S_OK; }
|
||||
IFACEMETHODIMP OnShareViolation( IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE* ) { return S_OK; }
|
||||
IFACEMETHODIMP OnTypeChange( IFileDialog*pfd ) { return S_OK; }
|
||||
IFACEMETHODIMP OnOverwrite( IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE* ) { return S_OK; }
|
||||
|
||||
//---- IUnknown methods ----
|
||||
|
||||
IFACEMETHODIMP QueryInterface( REFIID riid, void** ppv ) {
|
||||
if( riid != IID_IFileDialogEvents && riid != IID_IUnknown )
|
||||
return E_NOINTERFACE;
|
||||
|
||||
*ppv = static_cast<IFileDialogEvents*>( this );
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef() {
|
||||
return InterlockedIncrement( &refCount );
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) Release() {
|
||||
LONG newRefCount = InterlockedDecrement( &refCount );
|
||||
if( newRefCount == 0 )
|
||||
delete this;
|
||||
return newRefCount;
|
||||
}
|
||||
|
||||
private:
|
||||
~DialogEventHandler() {}
|
||||
};
|
||||
|
||||
//---- class CoInitializer ----------------------------------------------------
|
||||
|
||||
class CoInitializer {
|
||||
@@ -163,7 +250,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr
|
||||
( JNIEnv* env, jclass cls, jobject owner, jboolean open,
|
||||
jstring title, jstring okButtonLabel, jstring fileNameLabel, jstring fileName,
|
||||
jstring folder, jstring saveAsItem, jstring defaultFolder, jstring defaultExtension,
|
||||
jint optionsSet, jint optionsClear, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
jint optionsSet, jint optionsClear, jobject callback, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
{
|
||||
// initialize COM library
|
||||
CoInitializer coInitializer;
|
||||
@@ -226,20 +313,31 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr
|
||||
CHECK_HRESULT( dialog->SetFileTypeIndex( min( fileTypeIndex + 1, specs.count ) ) );
|
||||
}
|
||||
|
||||
// add event handler
|
||||
AutoReleasePtr<DialogEventHandler> handler( new DialogEventHandler( env, open, callback ) );
|
||||
DWORD dwCookie = 0;
|
||||
CHECK_HRESULT( dialog->Advise( handler, &dwCookie ) );
|
||||
|
||||
// show dialog
|
||||
HWND hwndOwner = (owner != NULL) ? getWindowHandle( env, owner ) : NULL;
|
||||
HRESULT hr = dialog->Show( hwndOwner );
|
||||
dialog->Unadvise( dwCookie );
|
||||
if( hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
|
||||
return newJavaStringArray( env, 0 );
|
||||
CHECK_HRESULT( hr );
|
||||
|
||||
// convert shell items to Java string array
|
||||
// get selected files as Java string array
|
||||
return getFiles( env, open, dialog );
|
||||
}
|
||||
|
||||
static jobjectArray getFiles( JNIEnv* env, jboolean open, IFileDialog* dialog ) {
|
||||
if( open ) {
|
||||
AutoReleasePtr<IShellItemArray> shellItems;
|
||||
DWORD count;
|
||||
CHECK_HRESULT( ((IFileOpenDialog*)(IFileDialog*)dialog)->GetResults( &shellItems ) );
|
||||
CHECK_HRESULT( shellItems->GetCount( &count ) );
|
||||
|
||||
// convert shell items to Java string array
|
||||
jobjectArray array = newJavaStringArray( env, count );
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
AutoReleasePtr<IShellItem> shellItem;
|
||||
@@ -260,6 +358,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr
|
||||
CHECK_HRESULT( dialog->GetResult( &shellItem ) );
|
||||
CHECK_HRESULT( shellItem->GetDisplayName( SIGDN_FILESYSPATH, &path ) );
|
||||
|
||||
// convert shell item to Java string array
|
||||
jstring jpath = newJavaString( env, path );
|
||||
CoTaskMemFree( path );
|
||||
|
||||
@@ -270,3 +369,15 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showMessageDialog
|
||||
( JNIEnv* env, jclass cls, jlong hwndParent, jstring text, jstring caption, jint type )
|
||||
{
|
||||
// convert Java strings to C strings
|
||||
AutoReleaseString ctext( env, text );
|
||||
AutoReleaseString ccaption( env, caption );
|
||||
|
||||
return ::MessageBox( reinterpret_cast<HWND>( hwndParent ), ctext, ccaption, type );
|
||||
}
|
||||
|
||||
@@ -116,10 +116,18 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeWindowsLibrary
|
||||
* Method: showFileChooser
|
||||
* Signature: (Ljava/awt/Window;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;III[Ljava/lang/String;)[Ljava/lang/String;
|
||||
* Signature: (Ljava/awt/Window;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeWindowsLibrary/FileChooserCallback;I[Ljava/lang/String;)[Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showFileChooser
|
||||
(JNIEnv *, jclass, jobject, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jint, jobjectArray);
|
||||
(JNIEnv *, jclass, jobject, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeWindowsLibrary
|
||||
* Method: showMessageDialog
|
||||
* Signature: (JLjava/lang/String;Ljava/lang/String;I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showMessageDialog
|
||||
(JNIEnv *, jclass, jlong, jstring, jstring, jint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user