mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-10 22:17:13 -06:00
System File Chooser: implemented native bindings for GtkFileChooserDialog on Linux
This commit is contained in:
@@ -24,16 +24,25 @@ To build the library on Linux, some packages needs to be installed.
|
||||
### Ubuntu
|
||||
|
||||
`build-essential` contains GCC and development tools. `libxt-dev` contains the
|
||||
X11 toolkit development headers.
|
||||
X11 toolkit development headers. `libgtk-3-dev` contains the GTK toolkit
|
||||
development headers.
|
||||
|
||||
~~~
|
||||
sudo apt update
|
||||
sudo apt install build-essential libxt-dev
|
||||
sudo apt install build-essential libxt-dev libgtk-3-dev
|
||||
~~~
|
||||
|
||||
|
||||
### Fedora
|
||||
|
||||
~~~
|
||||
sudo dnf group install c-development
|
||||
sudo dnf install libXt-devel gtk3-devel
|
||||
~~~
|
||||
|
||||
|
||||
### CentOS
|
||||
|
||||
~~~
|
||||
sudo yum install libXt-devel
|
||||
sudo yum install libXt-devel gtk3-devel
|
||||
~~~
|
||||
|
||||
@@ -52,7 +52,17 @@ tasks {
|
||||
|
||||
includes.from(
|
||||
"${javaHome}/include",
|
||||
"${javaHome}/include/linux"
|
||||
"${javaHome}/include/linux",
|
||||
|
||||
// for GTK
|
||||
"/usr/include/gtk-3.0",
|
||||
"/usr/include/glib-2.0",
|
||||
"/usr/lib/x86_64-linux-gnu/glib-2.0/include",
|
||||
"/usr/include/gdk-pixbuf-2.0",
|
||||
"/usr/include/atk-1.0",
|
||||
"/usr/include/cairo",
|
||||
"/usr/include/pango-1.0",
|
||||
"/usr/include/harfbuzz",
|
||||
)
|
||||
|
||||
compilerArgs.addAll( toolChain.map {
|
||||
@@ -75,7 +85,7 @@ tasks {
|
||||
|
||||
linkerArgs.addAll( toolChain.map {
|
||||
when( it ) {
|
||||
is Gcc, is Clang -> listOf( "-L${jawtPath}", "-l${jawt}" )
|
||||
is Gcc, is Clang -> listOf( "-L${jawtPath}", "-l${jawt}", "-lgtk-3" )
|
||||
else -> emptyList()
|
||||
}
|
||||
} )
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
// increase this version if changing API or functionality of native library
|
||||
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeLinuxLibrary
|
||||
#define API_VERSION_LINUX 3001
|
||||
#define API_VERSION_LINUX 3002
|
||||
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2025 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <jawt.h>
|
||||
#include <linux/jawt_md.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include "com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
* @since 3.6
|
||||
*/
|
||||
|
||||
//---- class AutoReleaseStringUTF8 --------------------------------------------
|
||||
|
||||
class AutoReleaseStringUTF8 {
|
||||
JNIEnv* env;
|
||||
jstring javaString;
|
||||
const char* chars;
|
||||
|
||||
public:
|
||||
AutoReleaseStringUTF8( JNIEnv* _env, jstring _javaString ) {
|
||||
env = _env;
|
||||
javaString = _javaString;
|
||||
chars = (javaString != NULL) ? env->GetStringUTFChars( javaString, NULL ) : NULL;
|
||||
}
|
||||
~AutoReleaseStringUTF8() {
|
||||
if( chars != NULL )
|
||||
env->ReleaseStringUTFChars( javaString, chars );
|
||||
}
|
||||
operator const gchar*() { return chars; }
|
||||
};
|
||||
|
||||
//---- helper -----------------------------------------------------------------
|
||||
|
||||
#define isOptionSet( option ) ((optionsSet & com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_ ## option) != 0)
|
||||
#define isOptionClear( option ) ((optionsClear & com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_ ## option) != 0)
|
||||
#define isOptionSetOrClear( option ) isOptionSet( option ) || isOptionClear( option )
|
||||
|
||||
jobjectArray newJavaStringArray( JNIEnv* env, jsize count ) {
|
||||
jclass stringClass = env->FindClass( "java/lang/String" );
|
||||
return env->NewObjectArray( count, stringClass, NULL );
|
||||
}
|
||||
|
||||
void initFilters( GtkFileChooser* chooser, JNIEnv* env, jint fileTypeIndex, jobjectArray fileTypes ) {
|
||||
jint length = env->GetArrayLength( fileTypes );
|
||||
if( length <= 0 )
|
||||
return;
|
||||
|
||||
GtkFileFilter* filter = NULL;
|
||||
int filterIndex = 0;
|
||||
for( int i = 0; i < length; i++ ) {
|
||||
jstring jstr = (jstring) env->GetObjectArrayElement( fileTypes, i );
|
||||
if( jstr == NULL ) {
|
||||
if( filter != NULL ) {
|
||||
gtk_file_chooser_add_filter( chooser, filter );
|
||||
if( fileTypeIndex == filterIndex )
|
||||
gtk_file_chooser_set_filter( chooser, filter );
|
||||
filter = NULL;
|
||||
filterIndex++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
AutoReleaseStringUTF8 str( env, jstr );
|
||||
if( filter == NULL ) {
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, str );
|
||||
} else
|
||||
gtk_file_filter_add_pattern( filter, str );
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_response( GtkWidget* dialog, gint responseId, gpointer data ) {
|
||||
if( responseId == GTK_RESPONSE_ACCEPT )
|
||||
*((GSList**)data) = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER( dialog ) );
|
||||
|
||||
gtk_widget_hide( dialog );
|
||||
gtk_widget_destroy( dialog );
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
|
||||
( JNIEnv* env, jclass cls, jboolean open,
|
||||
jstring title, jstring okButtonLabel, jstring currentName, jstring currentFolder,
|
||||
jint optionsSet, jint optionsClear, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
{
|
||||
// initialize GTK
|
||||
if( !gtk_init_check( NULL, NULL ) )
|
||||
return NULL;
|
||||
|
||||
// convert Java strings to C strings
|
||||
AutoReleaseStringUTF8 ctitle( env, title );
|
||||
AutoReleaseStringUTF8 cokButtonLabel( env, okButtonLabel );
|
||||
AutoReleaseStringUTF8 ccurrentName( env, currentName );
|
||||
AutoReleaseStringUTF8 ccurrentFolder( env, currentFolder );
|
||||
|
||||
// create GTK file chooser dialog
|
||||
// https://docs.gtk.org/gtk3/class.FileChooserDialog.html
|
||||
bool selectFolder = isOptionSet( FC_select_folder );
|
||||
bool multiSelect = isOptionSet( FC_select_multiple );
|
||||
GtkWidget* dialog = gtk_file_chooser_dialog_new(
|
||||
(ctitle != NULL) ? ctitle
|
||||
: (open ? (selectFolder ? (multiSelect ? _("Select Folders") : _("Select Folder"))
|
||||
: (multiSelect ? _("Open Files") : _("Open File"))) : _("Save File")),
|
||||
NULL, // can not use AWT X11 window as parent because GtkWindow is required
|
||||
open ? (selectFolder ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : GTK_FILE_CHOOSER_ACTION_OPEN)
|
||||
: GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
(cokButtonLabel != NULL) ? cokButtonLabel : (open ? _("_Open") : _("_Save")), GTK_RESPONSE_ACCEPT,
|
||||
NULL ); // marks end of buttons
|
||||
GtkFileChooser* chooser = GTK_FILE_CHOOSER( dialog );
|
||||
|
||||
if( !open && ccurrentName != NULL )
|
||||
gtk_file_chooser_set_current_name( chooser, ccurrentName );
|
||||
if( ccurrentFolder != NULL )
|
||||
gtk_file_chooser_set_current_folder( chooser, ccurrentFolder );
|
||||
|
||||
if( isOptionSetOrClear( FC_select_multiple ) )
|
||||
gtk_file_chooser_set_select_multiple( chooser, isOptionSet( FC_select_multiple ) );
|
||||
if( isOptionSetOrClear( FC_show_hidden ) )
|
||||
gtk_file_chooser_set_show_hidden( chooser, isOptionSet( FC_show_hidden ) );
|
||||
if( isOptionSetOrClear( FC_local_only ) )
|
||||
gtk_file_chooser_set_local_only( chooser, isOptionSet( FC_local_only ) );
|
||||
if( isOptionSetOrClear( FC_do_overwrite_confirmation ) )
|
||||
gtk_file_chooser_set_do_overwrite_confirmation( chooser, isOptionSet( FC_do_overwrite_confirmation ) );
|
||||
if( isOptionSetOrClear( FC_create_folders ) )
|
||||
gtk_file_chooser_set_create_folders( chooser, isOptionSet( FC_create_folders ) );
|
||||
|
||||
initFilters( chooser, env, fileTypeIndex, fileTypes );
|
||||
|
||||
gtk_window_set_modal( GTK_WINDOW( dialog ), true );
|
||||
|
||||
// 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 );
|
||||
gtk_widget_show( dialog );
|
||||
gtk_main();
|
||||
|
||||
// canceled?
|
||||
if( fileList == NULL )
|
||||
return newJavaStringArray( env, 0 );
|
||||
|
||||
// convert GSList to Java string array
|
||||
guint count = g_slist_length( fileList );
|
||||
jobjectArray array = newJavaStringArray( env, count );
|
||||
GSList* it = fileList;
|
||||
for( int i = 0; i < count; i++, it = it->next ) {
|
||||
gchar* path = (gchar*) it->data;
|
||||
jstring jpath = env->NewStringUTF( path );
|
||||
g_free( path );
|
||||
|
||||
env->SetObjectArrayElement( array, i, jpath );
|
||||
env->DeleteLocalRef( jpath );
|
||||
}
|
||||
g_slist_free( fileList );
|
||||
return array;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ Window getWindowHandle( JNIEnv* env, JAWT* awt, jobject window, Display** displa
|
||||
/**
|
||||
* Send _NET_WM_MOVERESIZE to window to initiate moving or resizing.
|
||||
*
|
||||
* https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45446104441728
|
||||
* https://specifications.freedesktop.org/wm-spec/latest/ar01s04.html#id-1.5.4
|
||||
* https://gitlab.gnome.org/GNOME/gtk/-/blob/main/gdk/x11/gdksurface-x11.c#L3841-3881
|
||||
*/
|
||||
extern "C"
|
||||
|
||||
@@ -9,6 +9,18 @@ extern "C" {
|
||||
#endif
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_MOVE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_MOVE 8L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_select_folder
|
||||
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_select_folder 1L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_select_multiple
|
||||
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_select_multiple 2L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_show_hidden
|
||||
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_show_hidden 4L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_local_only
|
||||
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_local_only 8L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_do_overwrite_confirmation
|
||||
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_do_overwrite_confirmation 16L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_create_folders
|
||||
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_create_folders 32L
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary
|
||||
* Method: xMoveOrResizeWindow
|
||||
@@ -25,6 +37,14 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_xM
|
||||
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_xShowWindowMenu
|
||||
(JNIEnv *, jclass, jobject, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary
|
||||
* Method: showFileChooser
|
||||
* Signature: (ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;III[Ljava/lang/String;)[Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
|
||||
(JNIEnv *, jclass, jboolean, jstring, jstring, jstring, jstring, jint, jint, jint, jobjectArray);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user