mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-10 22:17:13 -06:00
Merge PR #988: System File Chooser
This commit is contained in:
@@ -25,6 +25,7 @@ To build the library on Linux, some packages needs to be installed:
|
||||
|
||||
- `build-essential` - GCC and development tools
|
||||
- `libxt-dev` - X11 toolkit development headers
|
||||
- `libgtk-3-dev` - GTK 3 toolkit development headers
|
||||
- `g++-aarch64-linux-gnu` - GNU C++ compiler for the arm64 architecture (only on
|
||||
x86_64 Linux for cross-compiling for arm64 architecture)
|
||||
|
||||
@@ -32,19 +33,39 @@ To build the library on Linux, some packages needs to be installed:
|
||||
### Ubuntu
|
||||
|
||||
~~~
|
||||
sudo apt update
|
||||
sudo apt install build-essential libxt-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential libxt-dev libgtk-3-dev
|
||||
~~~
|
||||
|
||||
Only on x86_64 Linux for cross-compiling for arm64 architecture:
|
||||
#### Cross-compile for arm64 architecture on x86_64 Linux
|
||||
|
||||
Only needed on x86_64 Linux if you want cross-compile for arm64 architecture:
|
||||
|
||||
~~~
|
||||
sudo apt install g++-aarch64-linux-gnu
|
||||
sudo apt-get install g++-aarch64-linux-gnu
|
||||
~~~
|
||||
|
||||
Download `libgtk-3.so` for arm64 architecture:
|
||||
|
||||
~~~
|
||||
cd flatlaf-natives/flatlaf-natives-linux/lib/aarch64
|
||||
wget --no-verbose https://ports.ubuntu.com/pool/main/g/gtk%2b3.0/libgtk-3-0_3.24.18-1ubuntu1_arm64.deb
|
||||
ar -x libgtk-3-0_3.24.18-1ubuntu1_arm64.deb data.tar.xz
|
||||
tar -xvf data.tar.xz --wildcards --to-stdout "./usr/lib/aarch64-linux-gnu/libgtk-3.so.0.*" > libgtk-3.so
|
||||
rm libgtk-3-0_3.24.18-1ubuntu1_arm64.deb data.tar.xz
|
||||
~~~
|
||||
|
||||
|
||||
### 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
|
||||
~~~
|
||||
|
||||
@@ -65,15 +65,37 @@ tasks {
|
||||
|
||||
includes.from(
|
||||
"${javaHome}/include",
|
||||
"${javaHome}/include/linux"
|
||||
"${javaHome}/include/linux",
|
||||
|
||||
// for GTK
|
||||
"/usr/include/gtk-3.0",
|
||||
"/usr/include/glib-2.0",
|
||||
if( name.contains( "X86-64" ) ) "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
|
||||
else "/usr/lib/aarch64-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 {
|
||||
when( it ) {
|
||||
is Gcc, is Clang -> listOf()
|
||||
is Gcc, is Clang -> listOf( "-fvisibility=hidden" )
|
||||
else -> emptyList()
|
||||
}
|
||||
} )
|
||||
|
||||
doFirst {
|
||||
// check required Java version
|
||||
if( JavaVersion.current() < JavaVersion.VERSION_11 ) {
|
||||
println()
|
||||
println( "WARNING: Java 11 or later required to build Linux native library (running ${System.getProperty( "java.version" )})" )
|
||||
println( " Native library built with older Java versions throw following exception when running in Java 17+:" )
|
||||
println( " java.lang.UnsatisfiedLinkError: .../libjawt.so: version `SUNWprivate_1.1' not found" )
|
||||
println()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
withType<LinkSharedLibrary>().configureEach {
|
||||
@@ -88,7 +110,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()
|
||||
}
|
||||
} )
|
||||
@@ -128,7 +150,20 @@ tasks {
|
||||
"-I", "${javaHome}/include/linux",
|
||||
"-I", "$include",
|
||||
|
||||
// for GTK
|
||||
"-I", "/usr/include/gtk-3.0",
|
||||
"-I", "/usr/include/glib-2.0",
|
||||
"-I", "/usr/lib/x86_64-linux-gnu/glib-2.0/include",
|
||||
"-I", "/usr/include/gdk-pixbuf-2.0",
|
||||
"-I", "/usr/include/atk-1.0",
|
||||
"-I", "/usr/include/cairo",
|
||||
"-I", "/usr/include/pango-1.0",
|
||||
"-I", "/usr/include/harfbuzz",
|
||||
|
||||
"$src/ApiVersion.cpp",
|
||||
"$src/GtkFileChooser.cpp",
|
||||
"$src/GtkMessageDialog.cpp",
|
||||
"$src/JNIUtils.cpp",
|
||||
"$src/X11WmUtils.cpp",
|
||||
)
|
||||
}
|
||||
@@ -152,10 +187,15 @@ tasks {
|
||||
"-o", "$outDir/$libraryName",
|
||||
|
||||
"$objDir/ApiVersion.o",
|
||||
"$objDir/GtkFileChooser.o",
|
||||
"$objDir/GtkMessageDialog.o",
|
||||
"$objDir/JNIUtils.o",
|
||||
"$objDir/X11WmUtils.o",
|
||||
|
||||
"-lstdc++",
|
||||
"-L${layout.projectDirectory}/lib/aarch64",
|
||||
"-ljawt",
|
||||
"-lgtk-3",
|
||||
)
|
||||
|
||||
doLast {
|
||||
|
||||
@@ -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,277 @@
|
||||
/*
|
||||
* 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 "JNIUtils.h"
|
||||
#include "com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
* @since 3.7
|
||||
*/
|
||||
|
||||
// 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 );
|
||||
|
||||
//---- 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 )
|
||||
|
||||
static jobjectArray newJavaStringArray( JNIEnv* env, jsize count ) {
|
||||
jclass stringClass = env->FindClass( "java/lang/String" );
|
||||
return env->NewObjectArray( count, stringClass, NULL );
|
||||
}
|
||||
|
||||
static 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 GdkWindow* getGdkWindow( JNIEnv* env, jobject window ) {
|
||||
// get the AWT
|
||||
JAWT awt;
|
||||
awt.version = JAWT_VERSION_1_4;
|
||||
if( !JAWT_GetAWT( env, &awt ) )
|
||||
return NULL;
|
||||
|
||||
// get Xlib window and display from AWT window
|
||||
Display* display;
|
||||
Window w = getWindowHandle( env, &awt, window, &display );
|
||||
if( w == 0 )
|
||||
return NULL;
|
||||
|
||||
// based on GetAllocNativeWindowHandle() from https://github.com/btzy/nativefiledialog-extended
|
||||
// https://github.com/btzy/nativefiledialog-extended/blob/29e3bcb578345b9fa345d1d7683f00c150565ca3/src/nfd_gtk.cpp#L384-L437
|
||||
GdkDisplay* gdkDisplay = gdk_x11_lookup_xdisplay( display );
|
||||
if( gdkDisplay == NULL ) {
|
||||
// search for existing X11 display (there should only be one, even if multiple screens are connected)
|
||||
GdkDisplayManager* displayManager = gdk_display_manager_get();
|
||||
GSList* displays = gdk_display_manager_list_displays( displayManager );
|
||||
for( GSList* l = displays; l; l = l->next ) {
|
||||
if( GDK_IS_X11_DISPLAY( l->data ) ) {
|
||||
gdkDisplay = GDK_DISPLAY( l->data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_slist_free( displays );
|
||||
|
||||
// create our own X11 display
|
||||
if( gdkDisplay == NULL ) {
|
||||
gdk_set_allowed_backends( "x11" );
|
||||
gdkDisplay = gdk_display_manager_open_display( displayManager, NULL );
|
||||
gdk_set_allowed_backends( NULL );
|
||||
|
||||
if( gdkDisplay == NULL )
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return gdk_x11_window_foreign_new_for_display( gdkDisplay, w );
|
||||
}
|
||||
|
||||
static void handle_realize( GtkWidget* dialog, gpointer data ) {
|
||||
GdkWindow* gdkOwner = static_cast<GdkWindow*>( data );
|
||||
|
||||
// make file dialog a transient of owner window,
|
||||
// which centers file dialog on owner and keeps file dialog above owner
|
||||
gdk_window_set_transient_for( gtk_widget_get_window( dialog ), gdkOwner );
|
||||
|
||||
// necessary because gdk_x11_window_foreign_new_for_display() increases the reference counter
|
||||
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 ) {
|
||||
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 );
|
||||
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, jobject owner, jboolean open,
|
||||
jstring title, jstring okButtonLabel, jstring currentName, jstring currentFolder,
|
||||
jint optionsSet, jint optionsClear, jobject callback, 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
|
||||
: (selectFolder ? (multiSelect ? _("Select Folders") : _("Select Folder"))
|
||||
: (open ? ((multiSelect ? _("Open Files") : _("Open File"))) : _("Save File"))),
|
||||
NULL, // can not use AWT X11 window as parent because GtkWindow is required
|
||||
selectFolder ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
|
||||
: (open ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE),
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
(cokButtonLabel != NULL) ? cokButtonLabel
|
||||
: (selectFolder ? _("_Select") : (open ? _("_Open") : _("_Save"))), GTK_RESPONSE_ACCEPT,
|
||||
NULL ); // marks end of buttons
|
||||
GtkFileChooser* chooser = GTK_FILE_CHOOSER( dialog );
|
||||
|
||||
// set current name and folder
|
||||
if( !open && ccurrentName != NULL )
|
||||
gtk_file_chooser_set_current_name( chooser, ccurrentName );
|
||||
if( ccurrentFolder != NULL )
|
||||
gtk_file_chooser_set_current_folder( chooser, ccurrentFolder );
|
||||
|
||||
// set options
|
||||
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 ) );
|
||||
|
||||
// initialize filter
|
||||
initFilters( chooser, env, fileTypeIndex, fileTypes );
|
||||
|
||||
// setup modality
|
||||
GdkWindow* gdkOwner = (owner != NULL) ? getGdkWindow( env, owner ) : NULL;
|
||||
if( gdkOwner != NULL ) {
|
||||
gtk_window_set_modal( GTK_WINDOW( dialog ), true );
|
||||
|
||||
// file dialog should use same screen as owner
|
||||
gtk_window_set_screen( GTK_WINDOW( dialog ), gdk_window_get_screen( gdkOwner ) );
|
||||
|
||||
// set the transient when the file dialog is realized
|
||||
g_signal_connect( dialog, "realize", G_CALLBACK( handle_realize ), gdkOwner );
|
||||
}
|
||||
|
||||
// show dialog
|
||||
// (similar to what's done in sun_awt_X11_GtkFileDialogPeer.c)
|
||||
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)
|
||||
// see issues:
|
||||
// https://github.com/btzy/nativefiledialog-extended/issues/31
|
||||
// https://github.com/mlabbe/nativefiledialog/pull/92
|
||||
// https://github.com/guillaumechereau/noc/pull/11
|
||||
if( GDK_IS_X11_DISPLAY( gtk_widget_get_display( GTK_WIDGET( dialog ) ) ) ) {
|
||||
GdkWindow* gdkWindow = gtk_widget_get_window( GTK_WIDGET( dialog ) );
|
||||
gdk_window_set_events( gdkWindow, static_cast<GdkEventMask>( gdk_window_get_events( gdkWindow ) | GDK_PROPERTY_CHANGE_MASK ) );
|
||||
gtk_window_present_with_time( GTK_WINDOW( dialog ), gdk_x11_get_server_time( gdkWindow ) );
|
||||
}
|
||||
|
||||
// start event loop (will be quit in respone handler)
|
||||
gtk_main();
|
||||
|
||||
// canceled?
|
||||
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;
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 "JNIUtils.h"
|
||||
#include "com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
* @since 3.7
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
// avoid inlining of printf()
|
||||
#define _NO_CRT_STDIO_INLINE
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include "JNIUtils.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
//---- class AutoReleaseStringUTF8 --------------------------------------------
|
||||
|
||||
AutoReleaseStringUTF8::AutoReleaseStringUTF8( JNIEnv* _env, jstring _javaString ) {
|
||||
env = _env;
|
||||
javaString = _javaString;
|
||||
chars = (javaString != NULL) ? env->GetStringUTFChars( javaString, NULL ) : NULL;
|
||||
}
|
||||
|
||||
AutoReleaseStringUTF8::~AutoReleaseStringUTF8() {
|
||||
if( chars != NULL )
|
||||
env->ReleaseStringUTFChars( javaString, chars );
|
||||
}
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_isLibAvailable
|
||||
( JNIEnv* env, jclass cls, jstring libname )
|
||||
{
|
||||
AutoReleaseStringUTF8 clibname( env, libname );
|
||||
|
||||
void* lib = dlopen( clibname, RTLD_LAZY );
|
||||
if( lib == NULL )
|
||||
return false;
|
||||
|
||||
dlclose( lib );
|
||||
return true;
|
||||
}
|
||||
@@ -25,18 +25,21 @@
|
||||
*/
|
||||
|
||||
|
||||
bool sendEvent( JNIEnv *env, jobject window, const char *atom_name,
|
||||
long data0, long data1, long data2, long data3, long data4 );
|
||||
bool isWMHintSupported( Display* display, Window rootWindow, Atom atom );
|
||||
// declare exported methods
|
||||
Window getWindowHandle( JNIEnv* env, JAWT* awt, jobject window, Display** display_return );
|
||||
|
||||
// declare internal methods
|
||||
static bool sendEvent( JNIEnv *env, jobject window, const char *atom_name,
|
||||
long data0, long data1, long data2, long data3, long data4 );
|
||||
static bool isWMHintSupported( Display* display, Window rootWindow, Atom atom );
|
||||
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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"
|
||||
@@ -79,7 +82,7 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_xS
|
||||
0 );
|
||||
}
|
||||
|
||||
bool sendEvent( JNIEnv *env, jobject window, const char *atom_name,
|
||||
static bool sendEvent( JNIEnv *env, jobject window, const char *atom_name,
|
||||
long data0, long data1, long data2, long data3, long data4 )
|
||||
{
|
||||
// get the AWT
|
||||
@@ -131,7 +134,7 @@ bool sendEvent( JNIEnv *env, jobject window, const char *atom_name,
|
||||
}
|
||||
|
||||
|
||||
bool isWMHintSupported( Display* display, Window rootWindow, Atom atom ) {
|
||||
static bool isWMHintSupported( Display* display, Window rootWindow, Atom atom ) {
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long n_atoms;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 <gtk/gtk.h>
|
||||
#include <jni.h>
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
//---- class AutoReleaseStringUTF8 --------------------------------------------
|
||||
|
||||
class AutoReleaseStringUTF8 {
|
||||
JNIEnv* env;
|
||||
jstring javaString;
|
||||
const char* chars;
|
||||
|
||||
public:
|
||||
AutoReleaseStringUTF8( JNIEnv* _env, jstring _javaString );
|
||||
~AutoReleaseStringUTF8();
|
||||
|
||||
operator const gchar*() { return chars; }
|
||||
};
|
||||
@@ -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,30 @@ 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: isLibAvailable
|
||||
* Signature: (Ljava/lang/String;)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_isLibAvailable
|
||||
(JNIEnv *, jclass, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary
|
||||
* Method: showFileChooser
|
||||
* 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, 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
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -75,7 +75,7 @@ tasks {
|
||||
|
||||
compilerArgs.addAll( toolChain.map {
|
||||
when( it ) {
|
||||
is Gcc, is Clang -> listOf( "-x", "objective-c++", "-mmacosx-version-min=$minOs" )
|
||||
is Gcc, is Clang -> listOf( "-x", "objective-c++", "-mmacosx-version-min=$minOs", "-fvisibility=hidden" )
|
||||
else -> emptyList()
|
||||
}
|
||||
} )
|
||||
|
||||
@@ -46,7 +46,33 @@
|
||||
JNI_COCOA_CATCH() \
|
||||
}
|
||||
|
||||
#define JNI_THREAD_ENTER( jvm, returnValue ) \
|
||||
JNIEnv *env; \
|
||||
bool detach = false; \
|
||||
switch( jvm->GetEnv( (void**) &env, JNI_VERSION_1_6 ) ) { \
|
||||
case JNI_OK: break; \
|
||||
case JNI_EDETACHED: \
|
||||
if( jvm->AttachCurrentThread( (void**) &env, NULL ) != JNI_OK ) \
|
||||
return returnValue; \
|
||||
detach = true; \
|
||||
break; \
|
||||
default: return returnValue; \
|
||||
} \
|
||||
@try {
|
||||
|
||||
#define JNI_THREAD_EXIT( jvm ) \
|
||||
} @finally { \
|
||||
if( env->ExceptionCheck() ) \
|
||||
env->ExceptionDescribe(); \
|
||||
if( detach ) \
|
||||
jvm->DetachCurrentThread(); \
|
||||
}
|
||||
|
||||
|
||||
jclass findClass( JNIEnv *env, const char* className, bool globalRef );
|
||||
jfieldID getFieldID( JNIEnv *env, jclass cls, const char* fieldName, const char* fieldSignature, bool staticField );
|
||||
jmethodID getMethodID( JNIEnv *env, jclass cls, const char* methodName, const char* methodSignature, bool staticMethod );
|
||||
|
||||
NSString* JavaToNSString( JNIEnv *env, jstring javaString );
|
||||
jstring NSToJavaString( JNIEnv *env, NSString *nsString );
|
||||
jstring NormalizedPathJavaFromNSString( JNIEnv* env, NSString *nsString );
|
||||
|
||||
@@ -13,6 +13,32 @@ extern "C" {
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_MEDIUM 1L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_LARGE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_LARGE 2L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseFiles
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseFiles 1L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseDirectories
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseDirectories 2L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_resolvesAliases
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_resolvesAliases 4L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsMultipleSelection
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsMultipleSelection 8L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_accessoryViewDisclosed
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_accessoryViewDisclosed 16L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsTagField
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsTagField 256L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canCreateDirectories
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canCreateDirectories 512L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canSelectHiddenExtension
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canSelectHiddenExtension 1024L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsHiddenFiles
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsHiddenFiles 2048L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_extensionHidden
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_extensionHidden 4096L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsOtherFileTypes
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsOtherFileTypes 8192L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_treatsFilePackagesAsDirectories
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_treatsFilePackagesAsDirectories 16384L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showSingleFilterField
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showSingleFilterField 16777216L
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
|
||||
* Method: setWindowRoundedBorder
|
||||
@@ -53,6 +79,22 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_isWi
|
||||
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_toggleWindowFullScreen
|
||||
(JNIEnv *, jclass, jobject);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
|
||||
* Method: showFileChooser
|
||||
* Signature: (Ljava/awt/Window;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeMacLibrary/FileChooserCallback;I[Ljava/lang/String;)[Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_showFileChooser
|
||||
(JNIEnv *, jclass, jobject, jint, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
|
||||
* Method: showMessageDialog
|
||||
* Signature: (JILjava/lang/String;Ljava/lang/String;I[Ljava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_showMessageDialog
|
||||
(JNIEnv *, jclass, jlong, jint, jstring, jstring, jint, jobjectArray);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
|
||||
#import <jni.h>
|
||||
#import "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
@@ -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.FlatNativeMacLibrary
|
||||
#define API_VERSION_MACOS 2001
|
||||
#define API_VERSION_MACOS 2002
|
||||
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
@@ -75,3 +75,38 @@ jmethodID getMethodID( JNIEnv *env, jclass cls, const char* methodName, const ch
|
||||
|
||||
return methodID;
|
||||
}
|
||||
|
||||
NSString* JavaToNSString( JNIEnv *env, jstring javaString ) {
|
||||
if( javaString == NULL )
|
||||
return NULL;
|
||||
|
||||
int len = env->GetStringLength( javaString );
|
||||
const jchar* chars = env->GetStringChars( javaString, NULL );
|
||||
if( chars == NULL )
|
||||
return NULL;
|
||||
|
||||
NSString* nsString = [NSString stringWithCharacters:(unichar*)chars length:len];
|
||||
env->ReleaseStringChars( javaString, chars );
|
||||
return nsString;
|
||||
}
|
||||
|
||||
jstring NSToJavaString( JNIEnv *env, NSString *nsString ) {
|
||||
if( nsString == NULL )
|
||||
return NULL;
|
||||
|
||||
jsize len = [nsString length];
|
||||
unichar* buffer = (unichar*) calloc( len, sizeof( unichar ) );
|
||||
if( buffer == NULL )
|
||||
return NULL;
|
||||
|
||||
[nsString getCharacters:buffer];
|
||||
jstring javaString = env->NewString( buffer, len );
|
||||
free( buffer );
|
||||
return javaString;
|
||||
}
|
||||
|
||||
jstring NormalizedPathJavaFromNSString( JNIEnv* env, NSString *nsString ) {
|
||||
return (nsString != NULL)
|
||||
? NSToJavaString( env, [nsString precomposedStringWithCanonicalMapping] )
|
||||
: NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <jni.h>
|
||||
#import "JNIUtils.h"
|
||||
#import "JNFRunLoop.h"
|
||||
#import "com_formdev_flatlaf_ui_FlatNativeMacLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
* @since 3.7
|
||||
*/
|
||||
|
||||
// declare internal methods
|
||||
static jobjectArray newJavaStringArray( JNIEnv* env, jsize count );
|
||||
static jobjectArray urlsToStringArray( JNIEnv* env, NSArray* urls );
|
||||
static NSArray* getDialogURLs( NSSavePanel* dialog );
|
||||
|
||||
//---- class FileChooserDelegate ----------------------------------------------
|
||||
|
||||
@interface FileChooserDelegate : NSObject <NSOpenSavePanelDelegate, NSWindowDelegate> {
|
||||
NSArray* _filters;
|
||||
|
||||
JavaVM* _jvm;
|
||||
jobject _callback;
|
||||
NSMutableSet* _urlsSet;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) NSSavePanel* dialog;
|
||||
|
||||
- (void) initFilterAccessoryView: (NSMutableArray*)filters :(int)filterIndex
|
||||
:(NSString*)filterFieldLabel :(bool)showSingleFilterField;
|
||||
- (void) selectFormat: (id)sender;
|
||||
- (void) selectFormatAtIndex: (int)index;
|
||||
@end
|
||||
|
||||
@implementation FileChooserDelegate
|
||||
|
||||
- (void) initFilterAccessoryView: (NSMutableArray*)filters :(int)filterIndex
|
||||
:(NSString*)filterFieldLabel :(bool)showSingleFilterField
|
||||
{
|
||||
_filters = filters;
|
||||
|
||||
// get filter names
|
||||
NSArray* filterNames = filters.lastObject;
|
||||
[filters removeLastObject];
|
||||
|
||||
// do not add filter/format combobox if there is only one filter
|
||||
if( filters.count <= 1 && !showSingleFilterField ) {
|
||||
[self selectFormatAtIndex:0];
|
||||
return;
|
||||
}
|
||||
|
||||
// create label
|
||||
NSTextField* label = [[NSTextField alloc] initWithFrame:NSZeroRect];
|
||||
label.stringValue = (filterFieldLabel != NULL) ? filterFieldLabel : @"Format:";
|
||||
label.editable = NO;
|
||||
label.bordered = NO;
|
||||
label.bezeled = NO;
|
||||
label.drawsBackground = NO;
|
||||
|
||||
// create combobox
|
||||
NSPopUpButton* popupButton = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
|
||||
[popupButton addItemsWithTitles:filterNames];
|
||||
[popupButton selectItemAtIndex:MIN( MAX( filterIndex, 0 ), filterNames.count - 1 )];
|
||||
[popupButton setTarget:self];
|
||||
[popupButton setAction:@selector(selectFormat:)];
|
||||
|
||||
// create view
|
||||
NSView* accessoryView = [[NSView alloc] initWithFrame:NSZeroRect];
|
||||
[accessoryView addSubview:label];
|
||||
[accessoryView addSubview:popupButton];
|
||||
|
||||
// autolayout
|
||||
label.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
popupButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
int labelWidth = label.intrinsicContentSize.width;
|
||||
int gap = 12;
|
||||
int popupButtonWidth = popupButton.intrinsicContentSize.width;
|
||||
int popupButtonMinimumWidth = 140;
|
||||
int totalWidth = labelWidth + gap + MAX( popupButtonWidth, popupButtonMinimumWidth );
|
||||
[accessoryView addConstraints:@[
|
||||
// horizontal layout
|
||||
[label.leadingAnchor constraintEqualToAnchor:accessoryView.centerXAnchor constant:-(totalWidth / 2)],
|
||||
[popupButton.leadingAnchor constraintEqualToAnchor:label.trailingAnchor constant:gap],
|
||||
[popupButton.widthAnchor constraintGreaterThanOrEqualToConstant:popupButtonMinimumWidth],
|
||||
|
||||
// vertical layout
|
||||
[popupButton.topAnchor constraintEqualToAnchor:accessoryView.topAnchor constant:8],
|
||||
[popupButton.bottomAnchor constraintEqualToAnchor:accessoryView.bottomAnchor constant:-8],
|
||||
[label.firstBaselineAnchor constraintEqualToAnchor:popupButton.firstBaselineAnchor],
|
||||
]];
|
||||
|
||||
[_dialog setAccessoryView:accessoryView];
|
||||
|
||||
// initial filter
|
||||
[self selectFormatAtIndex:filterIndex];
|
||||
}
|
||||
|
||||
- (void) selectFormat: (id)sender {
|
||||
NSPopUpButton* popupButton = (NSPopUpButton*) sender;
|
||||
[self selectFormatAtIndex:popupButton.indexOfSelectedItem];
|
||||
}
|
||||
|
||||
- (void) selectFormatAtIndex: (int)index {
|
||||
index = MIN( MAX( index, 0 ), _filters.count - 1 );
|
||||
NSArray* fileTypes = [_filters objectAtIndex:index];
|
||||
|
||||
// use deprecated allowedFileTypes instead of newer allowedContentTypes (since macOS 11+)
|
||||
// to support older macOS versions 10.14+ and because of some problems with allowedContentTypes:
|
||||
// https://github.com/chromium/chromium/blob/d8e0032963b7ca4728ff4117933c0feb3e479b7a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm#L209-232
|
||||
_dialog.allowedFileTypes = [fileTypes containsObject:@"*"] ? nil : fileTypes;
|
||||
}
|
||||
|
||||
//---- NSOpenSavePanelDelegate ----
|
||||
|
||||
- (void) initCallback: (JavaVM*)jvm :(jobject)callback {
|
||||
_jvm = jvm;
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
- (BOOL) panel: (id) sender validateURL:(NSURL*) url error:(NSError**) outError {
|
||||
JNI_COCOA_TRY()
|
||||
|
||||
if( _callback == NULL )
|
||||
return true;
|
||||
|
||||
NSArray* urls = getDialogURLs( sender );
|
||||
|
||||
// if multiple files are selected for opening, then the validateURL method
|
||||
// is invoked for earch file, but our callback should be invoked only once for all files
|
||||
if( urls != NULL && urls.count > 1 ) {
|
||||
if( _urlsSet == NULL ) {
|
||||
// invoked for first selected file --> invoke callback
|
||||
_urlsSet = [NSMutableSet setWithArray:urls];
|
||||
[_urlsSet removeObject:url];
|
||||
} else {
|
||||
// invoked for other selected files --> do not invoke callback
|
||||
[_urlsSet removeObject:url];
|
||||
if( _urlsSet.count == 0 )
|
||||
_urlsSet = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
JNI_THREAD_ENTER( _jvm, true )
|
||||
|
||||
jobjectArray files = urlsToStringArray( env, urls );
|
||||
jlong window = (jlong) sender;
|
||||
|
||||
// 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 && !env->CallBooleanMethod( _callback, approveID, files, window ) ) {
|
||||
_urlsSet = NULL;
|
||||
return false; // keep dialog open
|
||||
}
|
||||
|
||||
JNI_THREAD_EXIT( _jvm )
|
||||
JNI_COCOA_CATCH()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---- NSWindowDelegate ----
|
||||
|
||||
- (void) windowDidBecomeMain:(NSNotification *) notification {
|
||||
JNI_COCOA_TRY()
|
||||
|
||||
// Disable main menu bar because the file dialog is modal and it should be not possible
|
||||
// to select any menu item. Otherwiese an action could show a Swing dialog, which would
|
||||
// be shown under the file dialog.
|
||||
//
|
||||
// NOTE: It is not necessary to re-enable the main menu bar because Swing does this itself.
|
||||
// When the file dialog is closed and a Swing window becomes active,
|
||||
// macOS sends windowDidBecomeMain (and windowDidBecomeKey) message to AWTWindow,
|
||||
// which invokes [self activateWindowMenuBar],
|
||||
// which invokes [CMenuBar activate:menuBar modallyDisabled:isDisabled],
|
||||
// which updates main menu bar.
|
||||
NSMenu* mainMenu = [NSApp mainMenu];
|
||||
int count = [mainMenu numberOfItems];
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
NSMenuItem* menuItem = [mainMenu itemAtIndex:i];
|
||||
NSMenu *subenu = [menuItem submenu];
|
||||
if( [subenu isJavaMenu] )
|
||||
[menuItem setEnabled:NO];
|
||||
}
|
||||
|
||||
JNI_COCOA_CATCH()
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//---- helper -----------------------------------------------------------------
|
||||
|
||||
#define isOptionSet( option ) ((optionsSet & com_formdev_flatlaf_ui_FlatNativeMacLibrary_ ## option) != 0)
|
||||
#define isOptionClear( option ) ((optionsClear & com_formdev_flatlaf_ui_FlatNativeMacLibrary_ ## option) != 0)
|
||||
#define isOptionSetOrClear( option ) isOptionSet( option ) || isOptionClear( option )
|
||||
|
||||
static jobjectArray newJavaStringArray( JNIEnv* env, jsize count ) {
|
||||
jclass stringClass = env->FindClass( "java/lang/String" );
|
||||
return env->NewObjectArray( count, stringClass, NULL );
|
||||
}
|
||||
|
||||
static NSMutableArray* initFilters( JNIEnv* env, jobjectArray fileTypes ) {
|
||||
jint length = env->GetArrayLength( fileTypes );
|
||||
if( length <= 0 )
|
||||
return NULL;
|
||||
|
||||
NSMutableArray* filterNames = [NSMutableArray array];
|
||||
NSMutableArray* filters = [NSMutableArray array];
|
||||
NSString* filterName = NULL;
|
||||
NSMutableArray* filter = NULL;
|
||||
for( int i = 0; i < length; i++ ) {
|
||||
jstring jstr = (jstring) env->GetObjectArrayElement( fileTypes, i );
|
||||
if( jstr == NULL ) {
|
||||
if( filter != NULL ) {
|
||||
if( filter.count > 0 ) {
|
||||
[filterNames addObject:filterName];
|
||||
[filters addObject:filter];
|
||||
}
|
||||
filterName = NULL;
|
||||
filter = NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString* str = JavaToNSString( env, jstr );
|
||||
env->DeleteLocalRef( jstr );
|
||||
if( filter == NULL ) {
|
||||
filterName = str;
|
||||
filter = [NSMutableArray array];
|
||||
} else
|
||||
[filter addObject:str];
|
||||
}
|
||||
|
||||
if( filters.count == 0 )
|
||||
return NULL;
|
||||
|
||||
// add filter names to array (removed again after creating combobox)
|
||||
[filters addObject:filterNames];
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_showFileChooser
|
||||
( JNIEnv* env, jclass cls, jobject owner, jint dark, jboolean open,
|
||||
jstring title, jstring prompt, jstring message, jstring filterFieldLabel,
|
||||
jstring nameFieldLabel, jstring nameFieldStringValue, jstring directoryURL,
|
||||
jint optionsSet, jint optionsClear, jobject callback, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
{
|
||||
JNI_COCOA_ENTER()
|
||||
|
||||
JavaVM* jvm;
|
||||
if( env->GetJavaVM( &jvm ) != JNI_OK )
|
||||
return NULL;
|
||||
|
||||
// convert Java strings to NSString (on Java thread)
|
||||
NSString* nsTitle = JavaToNSString( env, title );
|
||||
NSString* nsPrompt = JavaToNSString( env, prompt );
|
||||
NSString* nsMessage = JavaToNSString( env, message );
|
||||
NSString* nsFilterFieldLabel = JavaToNSString( env, filterFieldLabel );
|
||||
NSString* nsNameFieldLabel = JavaToNSString( env, nameFieldLabel );
|
||||
NSString* nsNameFieldStringValue = JavaToNSString( env, nameFieldStringValue );
|
||||
NSString* nsDirectoryURL = JavaToNSString( env, directoryURL );
|
||||
NSMutableArray* filters = initFilters( env, fileTypes );
|
||||
|
||||
NSArray* urls = NULL;
|
||||
NSArray** purls = &urls;
|
||||
|
||||
// show file dialog on macOS thread
|
||||
[FlatJNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
|
||||
JNI_COCOA_TRY()
|
||||
|
||||
// create open/save panel
|
||||
NSSavePanel* dialog = open ? [NSOpenPanel openPanel] : [NSSavePanel savePanel];
|
||||
|
||||
// set appearance
|
||||
if( dark == 1 )
|
||||
dialog.appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
|
||||
else if( dark == 0 )
|
||||
dialog.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
|
||||
|
||||
if( nsTitle != NULL )
|
||||
dialog.title = nsTitle;
|
||||
if( nsPrompt != NULL )
|
||||
dialog.prompt = nsPrompt;
|
||||
if( nsMessage != NULL )
|
||||
dialog.message = nsMessage;
|
||||
if( nsNameFieldLabel != NULL )
|
||||
dialog.nameFieldLabel = nsNameFieldLabel;
|
||||
if( nsNameFieldStringValue != NULL )
|
||||
dialog.nameFieldStringValue = nsNameFieldStringValue;
|
||||
if( nsDirectoryURL != NULL )
|
||||
dialog.directoryURL = [NSURL fileURLWithPath:nsDirectoryURL isDirectory:YES];
|
||||
|
||||
// set open options
|
||||
if( open ) {
|
||||
NSOpenPanel* openDialog = (NSOpenPanel*) dialog;
|
||||
|
||||
bool canChooseFiles = isOptionSet( FC_canChooseFiles );
|
||||
bool canChooseDirectories = isOptionSet( FC_canChooseDirectories );
|
||||
if( !canChooseFiles && !canChooseDirectories )
|
||||
canChooseFiles = true;
|
||||
openDialog.canChooseFiles = canChooseFiles;
|
||||
openDialog.canChooseDirectories = canChooseDirectories;
|
||||
|
||||
if( isOptionSetOrClear( FC_resolvesAliases ) )
|
||||
openDialog.resolvesAliases = isOptionSet( FC_resolvesAliases );
|
||||
if( isOptionSetOrClear( FC_allowsMultipleSelection ) )
|
||||
openDialog.allowsMultipleSelection = isOptionSet( FC_allowsMultipleSelection );
|
||||
}
|
||||
|
||||
// set options
|
||||
if( isOptionSetOrClear( FC_showsTagField ) )
|
||||
dialog.showsTagField = isOptionSet( FC_showsTagField );
|
||||
if( isOptionSetOrClear( FC_canCreateDirectories ) )
|
||||
dialog.canCreateDirectories = isOptionSet( FC_canCreateDirectories );
|
||||
if( isOptionSetOrClear( FC_canSelectHiddenExtension ) )
|
||||
dialog.canSelectHiddenExtension = isOptionSet( FC_canSelectHiddenExtension );
|
||||
if( isOptionSetOrClear( FC_showsHiddenFiles) )
|
||||
dialog.showsHiddenFiles = isOptionSet( FC_showsHiddenFiles);
|
||||
if( isOptionSetOrClear( FC_extensionHidden ) )
|
||||
dialog.extensionHidden = isOptionSet( FC_extensionHidden );
|
||||
if( isOptionSetOrClear( FC_allowsOtherFileTypes ) )
|
||||
dialog.allowsOtherFileTypes = isOptionSet( FC_allowsOtherFileTypes );
|
||||
if( isOptionSetOrClear( FC_treatsFilePackagesAsDirectories ) )
|
||||
dialog.treatsFilePackagesAsDirectories = isOptionSet( FC_treatsFilePackagesAsDirectories );
|
||||
|
||||
FileChooserDelegate* delegate = [FileChooserDelegate new];
|
||||
delegate.dialog = dialog;
|
||||
|
||||
// initialize filter accessory view
|
||||
if( filters != NULL ) {
|
||||
[delegate initFilterAccessoryView:filters :fileTypeIndex :nsFilterFieldLabel :isOptionSet( FC_showSingleFilterField )];
|
||||
|
||||
if( open && isOptionSetOrClear( FC_accessoryViewDisclosed ) )
|
||||
((NSOpenPanel*)dialog).accessoryViewDisclosed = isOptionSet( FC_accessoryViewDisclosed );
|
||||
}
|
||||
|
||||
// initialize callback
|
||||
if( callback != NULL )
|
||||
[delegate initCallback :jvm :callback];
|
||||
|
||||
// set file dialog delegate
|
||||
dialog.delegate = delegate;
|
||||
|
||||
// show dialog
|
||||
NSModalResponse response = [dialog runModal];
|
||||
[delegate release];
|
||||
if( response != NSModalResponseOK ) {
|
||||
*purls = @[];
|
||||
return;
|
||||
}
|
||||
|
||||
*purls = getDialogURLs( dialog );
|
||||
|
||||
JNI_COCOA_CATCH()
|
||||
}];
|
||||
|
||||
if( urls == NULL )
|
||||
return NULL;
|
||||
|
||||
// convert URLs to Java string array
|
||||
return urlsToStringArray( env, urls );
|
||||
|
||||
JNI_COCOA_EXIT()
|
||||
}
|
||||
|
||||
static NSArray* getDialogURLs( NSSavePanel* dialog ) {
|
||||
if( [dialog isKindOfClass:[NSOpenPanel class]] )
|
||||
return [[NSArray alloc] initWithArray: static_cast<NSOpenPanel*>(dialog).URLs];
|
||||
|
||||
NSURL* url = dialog.URL;
|
||||
// use '[[NSArray alloc] initWithObject:url]' here because '@[url]' crashes on macOS 10.14
|
||||
return (url != NULL) ? [[NSArray alloc] initWithObject:url] : @[];
|
||||
}
|
||||
|
||||
static jobjectArray urlsToStringArray( JNIEnv* env, NSArray* urls ) {
|
||||
jsize count = (urls != NULL) ? urls.count : 0;
|
||||
jobjectArray array = newJavaStringArray( env, count );
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
jstring filename = NormalizedPathJavaFromNSString( env, [urls[i] path] );
|
||||
env->SetObjectArrayElement( array, i, filename );
|
||||
env->DeleteLocalRef( filename );
|
||||
}
|
||||
return array;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <jni.h>
|
||||
#import "JNIUtils.h"
|
||||
#import "JNFRunLoop.h"
|
||||
#import "com_formdev_flatlaf_ui_FlatNativeMacLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
* @since 3.7
|
||||
*/
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_showMessageDialog
|
||||
( JNIEnv* env, jclass cls, jlong hwndParent, jint alertStyle, jstring messageText, jstring informativeText,
|
||||
jint defaultButton, jobjectArray buttons )
|
||||
{
|
||||
JNI_COCOA_ENTER()
|
||||
|
||||
// convert Java strings to NSString (on Java thread)
|
||||
NSString* nsMessageText = JavaToNSString( env, messageText );
|
||||
NSString* nsInformativeText = JavaToNSString( env, informativeText );
|
||||
|
||||
jint buttonCount = env->GetArrayLength( buttons );
|
||||
NSMutableArray* nsButtons = [NSMutableArray array];
|
||||
for( int i = 0; i < buttonCount; i++ ) {
|
||||
NSString* nsButton = JavaToNSString( env, (jstring) env->GetObjectArrayElement( buttons, i ) );
|
||||
[nsButtons addObject:nsButton];
|
||||
}
|
||||
|
||||
jint result = -1;
|
||||
jint* presult = &result;
|
||||
|
||||
// show alert on macOS thread
|
||||
[FlatJNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
|
||||
NSAlert* alert = [[NSAlert alloc] init];
|
||||
|
||||
// use appearance from parent window
|
||||
NSWindow* parent = (NSWindow*) hwndParent;
|
||||
if( parent != NULL )
|
||||
alert.window.appearance = parent.appearance;
|
||||
|
||||
// use empty string because if alert.messageText is not set it displays "Alert"
|
||||
alert.messageText = (nsMessageText != NULL) ? nsMessageText : @"";
|
||||
if( nsInformativeText != NULL )
|
||||
alert.informativeText = nsInformativeText;
|
||||
|
||||
// alert style
|
||||
switch( alertStyle ) {
|
||||
case /* JOptionPane.ERROR_MESSAGE */ 0: alert.alertStyle = NSAlertStyleCritical; break;
|
||||
default:
|
||||
case /* JOptionPane.INFORMATION_MESSAGE */ 1: alert.alertStyle = NSAlertStyleInformational; break;
|
||||
case /* JOptionPane.WARNING_MESSAGE */ 2: alert.alertStyle = NSAlertStyleWarning; break;
|
||||
}
|
||||
|
||||
// add buttons
|
||||
for( int i = 0; i < nsButtons.count; i++ ) {
|
||||
NSButton* b = [alert addButtonWithTitle:nsButtons[i]];
|
||||
if( i == defaultButton )
|
||||
alert.window.defaultButtonCell = b.cell;
|
||||
}
|
||||
|
||||
// show alert
|
||||
NSInteger response = [alert runModal];
|
||||
|
||||
// if no buttons added, which shows a single OK button, the response is 0 when clicking OK
|
||||
// if buttons added, response is 1000+buttonIndex
|
||||
*presult = MAX( response - NSAlertFirstButtonReturn, 0 );
|
||||
}];
|
||||
|
||||
return result;
|
||||
|
||||
JNI_COCOA_EXIT()
|
||||
}
|
||||
@@ -39,13 +39,15 @@
|
||||
@implementation WindowData
|
||||
@end
|
||||
|
||||
// declare internal methods
|
||||
// declare exported methods
|
||||
NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window );
|
||||
WindowData* getWindowData( NSWindow* nsWindow, bool allocate );
|
||||
void setWindowButtonsHidden( NSWindow* nsWindow, bool hidden );
|
||||
int getWindowButtonAreaWidth( NSWindow* nsWindow );
|
||||
int getWindowTitleBarHeight( NSWindow* nsWindow );
|
||||
bool isWindowFullScreen( NSWindow* nsWindow );
|
||||
|
||||
// declare internal methods
|
||||
static WindowData* getWindowData( NSWindow* nsWindow, bool allocate );
|
||||
static void setWindowButtonsHidden( NSWindow* nsWindow, bool hidden );
|
||||
static int getWindowButtonAreaWidth( NSWindow* nsWindow );
|
||||
static int getWindowTitleBarHeight( NSWindow* nsWindow );
|
||||
static bool isWindowFullScreen( NSWindow* nsWindow );
|
||||
|
||||
|
||||
NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
|
||||
@@ -79,7 +81,7 @@ NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
|
||||
return (NSWindow *) jlong_to_ptr( env->GetLongField( platformWindow, ptrID ) );
|
||||
}
|
||||
|
||||
WindowData* getWindowData( NSWindow* nsWindow, bool allocate ) {
|
||||
static WindowData* getWindowData( NSWindow* nsWindow, bool allocate ) {
|
||||
static char key;
|
||||
WindowData* windowData = objc_getAssociatedObject( nsWindow, &key );
|
||||
if( windowData == NULL && allocate ) {
|
||||
@@ -252,7 +254,7 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void setWindowButtonsHidden( NSWindow* nsWindow, bool hidden ) {
|
||||
static void setWindowButtonsHidden( NSWindow* nsWindow, bool hidden ) {
|
||||
// get buttons
|
||||
NSView* buttons[3] = {
|
||||
[nsWindow standardWindowButton:NSWindowCloseButton],
|
||||
@@ -312,7 +314,7 @@ JNIEXPORT jobject JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWi
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int getWindowButtonAreaWidth( NSWindow* nsWindow ) {
|
||||
static int getWindowButtonAreaWidth( NSWindow* nsWindow ) {
|
||||
// get buttons
|
||||
NSView* buttons[3] = {
|
||||
[nsWindow standardWindowButton:NSWindowCloseButton],
|
||||
@@ -344,7 +346,7 @@ int getWindowButtonAreaWidth( NSWindow* nsWindow ) {
|
||||
return right + left;
|
||||
}
|
||||
|
||||
int getWindowTitleBarHeight( NSWindow* nsWindow ) {
|
||||
static int getWindowTitleBarHeight( NSWindow* nsWindow ) {
|
||||
NSView* closeButton = [nsWindow standardWindowButton:NSWindowCloseButton];
|
||||
if( closeButton == NULL )
|
||||
return -1;
|
||||
@@ -369,7 +371,7 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_isWi
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool isWindowFullScreen( NSWindow* nsWindow ) {
|
||||
static bool isWindowFullScreen( NSWindow* nsWindow ) {
|
||||
return ((nsWindow.styleMask & NSWindowStyleMaskFullScreen) != 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
} )
|
||||
@@ -80,8 +80,8 @@ tasks {
|
||||
|
||||
linkerArgs.addAll( toolChain.map {
|
||||
when( it ) {
|
||||
is Gcc, is Clang -> listOf( "-lUser32", "-lGdi32", "-lshell32", "-lAdvAPI32", "-lKernel32", "-lDwmapi" )
|
||||
is VisualCpp -> listOf( "User32.lib", "Gdi32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "Dwmapi.lib", "/NODEFAULTLIB" )
|
||||
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" )
|
||||
else -> emptyList()
|
||||
}
|
||||
} )
|
||||
@@ -93,6 +93,15 @@ tasks {
|
||||
into( nativesDir )
|
||||
rename( linkedFile.get().asFile.name, libraryName )
|
||||
}
|
||||
|
||||
/*dump
|
||||
val dumpbin = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/bin/Hostx64/x64/dumpbin.exe"
|
||||
val dll = linkedFile.asFile.get()
|
||||
val dllDir = dll.parent
|
||||
exec { commandLine( dumpbin, "/all", "/rawdata:none", "/out:$dllDir/objdump.txt", dll ) }
|
||||
exec { commandLine( dumpbin, "/all", "/out:$dllDir/full-contents.txt", dll ) }
|
||||
exec { commandLine( dumpbin, "/disasm", "/out:$dllDir/disassemble.txt", dll ) }
|
||||
dump*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.FlatNativeWindowsLibrary
|
||||
#define API_VERSION_WINDOWS 1001
|
||||
#define API_VERSION_WINDOWS 1002
|
||||
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
// declare exported methods
|
||||
HWND getWindowHandle( JNIEnv* env, jobject window );
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
// avoid inlining of printf()
|
||||
#define _NO_CRT_STDIO_INLINE
|
||||
|
||||
#include "JNIUtils.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
//---- class AutoReleaseString ------------------------------------------------
|
||||
|
||||
AutoReleaseString::AutoReleaseString( JNIEnv* _env, jstring _javaString ) {
|
||||
env = _env;
|
||||
javaString = _javaString;
|
||||
chars = (javaString != NULL) ? env->GetStringChars( javaString, NULL ) : NULL;
|
||||
}
|
||||
|
||||
AutoReleaseString::~AutoReleaseString() {
|
||||
if( chars != NULL )
|
||||
env->ReleaseStringChars( javaString, chars );
|
||||
}
|
||||
|
||||
//---- class AutoReleaseStringArray -------------------------------------------
|
||||
|
||||
AutoReleaseStringArray::AutoReleaseStringArray( JNIEnv* _env, jobjectArray _javaStringArray ) {
|
||||
env = _env;
|
||||
count = (_javaStringArray != NULL) ? env->GetArrayLength( _javaStringArray ) : 0;
|
||||
if( count <= 0 )
|
||||
return;
|
||||
|
||||
javaStringArray = new jstring[count];
|
||||
charsArray = new const jchar*[count];
|
||||
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
javaStringArray[i] = (jstring) env->GetObjectArrayElement( _javaStringArray, i );
|
||||
charsArray[i] = env->GetStringChars( javaStringArray[i] , NULL );
|
||||
}
|
||||
}
|
||||
|
||||
AutoReleaseStringArray::~AutoReleaseStringArray() {
|
||||
if( count == 0 )
|
||||
return;
|
||||
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
env->ReleaseStringChars( javaStringArray[i], charsArray[i] );
|
||||
env->DeleteLocalRef( javaStringArray[i] );
|
||||
}
|
||||
|
||||
delete[] javaStringArray;
|
||||
delete[] charsArray;
|
||||
}
|
||||
@@ -36,8 +36,11 @@
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
HINSTANCE _instance;
|
||||
|
||||
extern "C"
|
||||
BOOL WINAPI _DllMainCRTStartup( HINSTANCE instance, DWORD reason, LPVOID reserved ) {
|
||||
_instance = instance;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
// avoid inlining of printf()
|
||||
#define _NO_CRT_STDIO_INLINE
|
||||
|
||||
#include <windows.h>
|
||||
#include <shobjidl.h>
|
||||
#include "JNIUtils.h"
|
||||
#include "com_formdev_flatlaf_ui_FlatNativeWindowsLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
* @since 3.7
|
||||
*/
|
||||
|
||||
// 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 {
|
||||
T* ptr;
|
||||
|
||||
public:
|
||||
AutoReleasePtr() {
|
||||
ptr = NULL;
|
||||
}
|
||||
AutoReleasePtr( T* p ) {
|
||||
ptr = p;
|
||||
ptr->AddRef();
|
||||
}
|
||||
~AutoReleasePtr() {
|
||||
if( ptr != NULL )
|
||||
ptr->Release();
|
||||
}
|
||||
T** operator&() { return &ptr; }
|
||||
T* operator->() { return ptr; }
|
||||
operator T*() { return ptr; }
|
||||
};
|
||||
|
||||
//---- class AutoReleaseIShellItem --------------------------------------------
|
||||
|
||||
class AutoReleaseIShellItem : public AutoReleasePtr<IShellItem> {
|
||||
public:
|
||||
AutoReleaseIShellItem( JNIEnv* env, jstring path ) {
|
||||
AutoReleaseString cpath( env, path );
|
||||
::SHCreateItemFromParsingName( cpath, NULL, IID_IShellItem, reinterpret_cast<void**>( &*this ) );
|
||||
}
|
||||
};
|
||||
|
||||
//---- class FilterSpec -------------------------------------------------------
|
||||
|
||||
class FilterSpec {
|
||||
AutoReleaseStringArray fileTypes;
|
||||
|
||||
public:
|
||||
UINT count = 0;
|
||||
COMDLG_FILTERSPEC* specs = NULL;
|
||||
|
||||
public:
|
||||
FilterSpec( JNIEnv* _env, jobjectArray _fileTypes )
|
||||
: fileTypes( _env, _fileTypes )
|
||||
{
|
||||
if( fileTypes.count == 0 )
|
||||
return;
|
||||
|
||||
count = fileTypes.count / 2;
|
||||
specs = new COMDLG_FILTERSPEC[fileTypes.count];
|
||||
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
specs[i].pszName = fileTypes[i * 2];
|
||||
specs[i].pszSpec = fileTypes[(i * 2) + 1];
|
||||
}
|
||||
}
|
||||
~FilterSpec() {
|
||||
if( specs != NULL )
|
||||
delete[] specs;
|
||||
}
|
||||
};
|
||||
|
||||
//---- 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 {
|
||||
public:
|
||||
bool initialized;
|
||||
|
||||
CoInitializer() {
|
||||
HRESULT result = ::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
|
||||
initialized = SUCCEEDED( result );
|
||||
}
|
||||
~CoInitializer() {
|
||||
if( initialized )
|
||||
::CoUninitialize();
|
||||
}
|
||||
};
|
||||
|
||||
//---- helper -----------------------------------------------------------------
|
||||
|
||||
#define isOptionSet( option ) ((optionsSet & com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_ ## option) != 0)
|
||||
#define CHECK_HRESULT( code ) { if( (code) != S_OK ) return NULL; }
|
||||
|
||||
static jobjectArray newJavaStringArray( JNIEnv* env, jsize count ) {
|
||||
jclass stringClass = env->FindClass( "java/lang/String" );
|
||||
return env->NewObjectArray( count, stringClass, NULL );
|
||||
}
|
||||
|
||||
static jstring newJavaString( JNIEnv* env, LPWSTR str ) {
|
||||
return env->NewString( reinterpret_cast<jchar*>( str ), static_cast<jsize>( wcslen( str ) ) );
|
||||
}
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showFileChooser
|
||||
( 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, jobject callback, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
{
|
||||
// initialize COM library
|
||||
CoInitializer coInitializer;
|
||||
if( !coInitializer.initialized )
|
||||
return NULL;
|
||||
|
||||
// handle limitations (without this, some Win32 method fails and this method returns NULL)
|
||||
if( isOptionSet( FOS_PICKFOLDERS ) ) {
|
||||
open = true; // always use IFileOpenDialog for picking folders
|
||||
fileTypes = NULL; // no filter allowed for picking folders
|
||||
}
|
||||
if( !open && isOptionSet( FOS_ALLOWMULTISELECT ) )
|
||||
optionsSet &= ~FOS_ALLOWMULTISELECT;
|
||||
|
||||
// convert Java strings to C strings
|
||||
AutoReleaseString ctitle( env, title );
|
||||
AutoReleaseString cokButtonLabel( env, okButtonLabel );
|
||||
AutoReleaseString cfileNameLabel( env, fileNameLabel );
|
||||
AutoReleaseString cfileName( env, fileName );
|
||||
AutoReleaseIShellItem cfolder( env, folder );
|
||||
AutoReleaseIShellItem csaveAsItem( env, saveAsItem );
|
||||
AutoReleaseIShellItem cdefaultFolder( env, defaultFolder );
|
||||
AutoReleaseString cdefaultExtension( env, defaultExtension );
|
||||
FilterSpec specs( env, fileTypes );
|
||||
|
||||
// create IFileOpenDialog or IFileSaveDialog
|
||||
// https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
||||
AutoReleasePtr<IFileDialog> dialog;
|
||||
CHECK_HRESULT( ::CoCreateInstance( open ? CLSID_FileOpenDialog : CLSID_FileSaveDialog,
|
||||
NULL, CLSCTX_INPROC_SERVER, open ? IID_IFileOpenDialog : IID_IFileSaveDialog,
|
||||
reinterpret_cast<LPVOID*>( &dialog ) ) );
|
||||
|
||||
// set title, etc.
|
||||
if( ctitle != NULL )
|
||||
CHECK_HRESULT( dialog->SetTitle( ctitle ) );
|
||||
if( cokButtonLabel != NULL )
|
||||
CHECK_HRESULT( dialog->SetOkButtonLabel( cokButtonLabel ) );
|
||||
if( cfileNameLabel != NULL )
|
||||
CHECK_HRESULT( dialog->SetFileNameLabel( cfileNameLabel ) );
|
||||
if( cfileName != NULL )
|
||||
CHECK_HRESULT( dialog->SetFileName( cfileName ) );
|
||||
if( cfolder != NULL )
|
||||
CHECK_HRESULT( dialog->SetFolder( cfolder ) );
|
||||
if( !open && csaveAsItem != NULL )
|
||||
CHECK_HRESULT( ((IFileSaveDialog*)(IFileDialog*)dialog)->SetSaveAsItem( csaveAsItem ) );
|
||||
if( cdefaultFolder != NULL )
|
||||
CHECK_HRESULT( dialog->SetDefaultFolder( cdefaultFolder ) );
|
||||
if( cdefaultExtension != NULL )
|
||||
CHECK_HRESULT( dialog->SetDefaultExtension( cdefaultExtension ) );
|
||||
|
||||
// set options
|
||||
FILEOPENDIALOGOPTIONS existingOptions;
|
||||
CHECK_HRESULT( dialog->GetOptions( &existingOptions ) );
|
||||
CHECK_HRESULT( dialog->SetOptions ( (existingOptions & ~optionsClear) | optionsSet ) );
|
||||
|
||||
// initialize filter
|
||||
if( specs.count > 0 ) {
|
||||
CHECK_HRESULT( dialog->SetFileTypes( specs.count, specs.specs ) );
|
||||
if( fileTypeIndex > 0 )
|
||||
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 );
|
||||
|
||||
// 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;
|
||||
LPWSTR path;
|
||||
CHECK_HRESULT( shellItems->GetItemAt( i, &shellItem ) );
|
||||
CHECK_HRESULT( shellItem->GetDisplayName( SIGDN_FILESYSPATH, &path ) );
|
||||
|
||||
jstring jpath = newJavaString( env, path );
|
||||
CoTaskMemFree( path );
|
||||
|
||||
env->SetObjectArrayElement( array, i, jpath );
|
||||
env->DeleteLocalRef( jpath );
|
||||
}
|
||||
return array;
|
||||
} else {
|
||||
AutoReleasePtr<IShellItem> shellItem;
|
||||
LPWSTR path;
|
||||
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 );
|
||||
|
||||
jobjectArray array = newJavaStringArray( env, 1 );
|
||||
env->SetObjectArrayElement( array, 0, jpath );
|
||||
env->DeleteLocalRef( jpath );
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// avoid inlining of printf()
|
||||
#define _NO_CRT_STDIO_INLINE
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include "JNIUtils.h"
|
||||
#include "com_formdev_flatlaf_ui_FlatNativeWindowsLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
* @since 3.7
|
||||
*/
|
||||
|
||||
#define ID_BUTTON1 101
|
||||
|
||||
// declare external fields
|
||||
extern HINSTANCE _instance;
|
||||
|
||||
// declare internal methods
|
||||
static byte* createInMemoryTemplate( HWND owner, int messageType, LPCWSTR title, LPCWSTR text,
|
||||
int defaultButton, int buttonCount, LPCWSTR* buttons );
|
||||
static INT_PTR CALLBACK messageDialogProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
||||
static int textLengthAsDLUs( HDC hdc, LPCWSTR str, int strLen );
|
||||
static LONG pixel2dluX( LONG px );
|
||||
static LONG pixel2dluY( LONG px );
|
||||
static LONG dluX2pixel( LONG dluX );
|
||||
static LPWORD lpwAlign( LPWORD lpIn );
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showMessageBox
|
||||
( 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 );
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showMessageDialog
|
||||
( JNIEnv* env, jclass cls, jlong hwndParent, jint messageType, jstring title,
|
||||
jstring text, jint defaultButton, jobjectArray buttons )
|
||||
{
|
||||
HWND owner = reinterpret_cast<HWND>( hwndParent );
|
||||
|
||||
// convert Java strings to C strings
|
||||
AutoReleaseString ctitle( env, title );
|
||||
AutoReleaseString ctext( env, text );
|
||||
AutoReleaseStringArray cbuttons( env, buttons );
|
||||
|
||||
// get title from parent window if necessary
|
||||
WCHAR parentTitle[100];
|
||||
if( ctitle == NULL )
|
||||
::GetWindowText( owner, parentTitle, 100 );
|
||||
|
||||
byte* templ = createInMemoryTemplate( owner, messageType, (ctitle != NULL) ? ctitle : parentTitle,
|
||||
ctext, defaultButton, cbuttons.count, cbuttons );
|
||||
if( templ == NULL )
|
||||
return -1;
|
||||
|
||||
LRESULT ret = ::DialogBoxIndirect( _instance, (LPDLGTEMPLATE) templ, owner, messageDialogProc );
|
||||
delete templ;
|
||||
return (ret >= ID_BUTTON1) ? ret - ID_BUTTON1 : -1;
|
||||
}
|
||||
|
||||
|
||||
// all values in DLUs
|
||||
|
||||
#define INSETS_TOP 12
|
||||
#define INSETS_LEFT 12
|
||||
#define INSETS_RIGHT 12
|
||||
#define INSETS_BOTTOM 6
|
||||
|
||||
#define ICON_TEXT_GAP 8
|
||||
|
||||
#define LABEL_MIN_WIDTH 100
|
||||
#define LABEL_MAX_WIDTH 250
|
||||
#define LABEL_HEIGHT 8
|
||||
|
||||
#define BUTTON_WIDTH 50
|
||||
#define BUTTON_HEIGHT 12
|
||||
#define BUTTON_GAP 5
|
||||
#define BUTTON_TOP_GAP 14
|
||||
#define BUTTON_LEFT_RIGHT_GAP 8
|
||||
|
||||
// based on https://learn.microsoft.com/en-us/windows/win32/dlgbox/using-dialog-boxes#creating-a-template-in-memory
|
||||
static byte* createInMemoryTemplate( HWND owner, int messageType, LPCWSTR title, LPCWSTR text,
|
||||
int defaultButton, int buttonCount, LPCWSTR* buttons )
|
||||
{
|
||||
// get font info needed for DS_SETFONT
|
||||
NONCLIENTMETRICS ncMetrics;
|
||||
ncMetrics.cbSize = sizeof( NONCLIENTMETRICS );
|
||||
if( !::SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncMetrics, 0 ) )
|
||||
return NULL;
|
||||
|
||||
// create DC to use message font
|
||||
HDC hdcOwner = ::GetDC( owner );
|
||||
HDC hdc = ::CreateCompatibleDC( hdcOwner );
|
||||
::ReleaseDC( owner, hdcOwner );
|
||||
if( hdc == NULL )
|
||||
return NULL;
|
||||
|
||||
HFONT hfont = ::CreateFontIndirect( &ncMetrics.lfMessageFont );
|
||||
if( hfont == NULL ) {
|
||||
::DeleteDC( hdc );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( ::SelectObject( hdc, hfont ) == NULL ) {
|
||||
::DeleteDC( hdc );
|
||||
::DeleteObject( hfont );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---- calculate layout (in DLUs) ----
|
||||
|
||||
// layout icon
|
||||
LPWSTR icon;
|
||||
switch( messageType ) {
|
||||
case /* JOptionPane.ERROR_MESSAGE */ 0: icon = IDI_ERROR; break;
|
||||
case /* JOptionPane.INFORMATION_MESSAGE */ 1: icon = IDI_INFORMATION; break;
|
||||
case /* JOptionPane.WARNING_MESSAGE */ 2: icon = IDI_WARNING; break;
|
||||
case /* JOptionPane.QUESTION_MESSAGE */ 3: icon = IDI_QUESTION; break;
|
||||
default:
|
||||
case /* JOptionPane.PLAIN_MESSAGE */ -1: icon = NULL; break;
|
||||
}
|
||||
int ix = INSETS_LEFT;
|
||||
int iy = INSETS_TOP;
|
||||
int iw = pixel2dluX( ::GetSystemMetrics( SM_CXICON ) );
|
||||
int ih = pixel2dluY( ::GetSystemMetrics( SM_CYICON ) );
|
||||
|
||||
// layout text
|
||||
int tx = ix + (icon != NULL ? iw + ICON_TEXT_GAP : 0);
|
||||
int ty = iy;
|
||||
int tw = 0;
|
||||
int th = 0;
|
||||
if( text == NULL )
|
||||
text = L"";
|
||||
LPWSTR wrappedText = new WCHAR[wcslen( text ) + 1];
|
||||
wcscpy( wrappedText, text );
|
||||
LPWSTR lineStart = wrappedText;
|
||||
for( LPWSTR t = wrappedText; ; t++ ) {
|
||||
if( *t != '\n' && *t != 0 )
|
||||
continue;
|
||||
|
||||
// calculate line width (in pixels) and number of charaters that fit into LABEL_MAX_WIDTH
|
||||
int lineLen = t - lineStart;
|
||||
int fit = 0;
|
||||
SIZE size{ 0 };
|
||||
if( !::GetTextExtentExPoint( hdc, lineStart, lineLen, dluX2pixel( LABEL_MAX_WIDTH ), &fit, NULL, &size ) )
|
||||
break;
|
||||
|
||||
if( fit < lineLen ) {
|
||||
// wrap too long line --> try to wrap at space character
|
||||
bool wrapped = false;
|
||||
for( LPWSTR t2 = lineStart + fit - 1; t2 > lineStart; t2-- ) {
|
||||
if( *t2 == ' ' || *t2 == '\t' ) {
|
||||
*t2 = '\n';
|
||||
int w = textLengthAsDLUs( hdc, lineStart, t2 - lineStart );
|
||||
tw = max( tw, w );
|
||||
th += LABEL_HEIGHT;
|
||||
|
||||
// continue wrapping after inserted line break
|
||||
t = t2;
|
||||
lineStart = t + 1;
|
||||
wrapped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !wrapped ) {
|
||||
// not able to wrap at word --> break long word
|
||||
int breakIndex = (lineStart + fit) - wrappedText;
|
||||
int w = textLengthAsDLUs( hdc, lineStart, breakIndex );
|
||||
tw = max( tw, w );
|
||||
th += LABEL_HEIGHT;
|
||||
|
||||
// duplicate string
|
||||
LPWSTR wrappedText2 = new WCHAR[wcslen( wrappedText ) + 1 + 1];
|
||||
// use wcscpy(), instead of wcsncpy(), because this method is inlined and does not require linking to runtime lib
|
||||
wcscpy( wrappedText2, wrappedText );
|
||||
wrappedText2[breakIndex] = '\n';
|
||||
wcscpy( wrappedText2 + breakIndex + 1, wrappedText + breakIndex );
|
||||
|
||||
// delete old text
|
||||
delete[] wrappedText;
|
||||
wrappedText = wrappedText2;
|
||||
|
||||
// continue wrapping after inserted line break
|
||||
t = wrappedText + breakIndex;
|
||||
lineStart = t + 1;
|
||||
}
|
||||
} else {
|
||||
// line fits into LABEL_MAX_WIDTH
|
||||
int w = pixel2dluX( size.cx );
|
||||
tw = max( tw, w );
|
||||
th += LABEL_HEIGHT;
|
||||
lineStart = t + 1;
|
||||
}
|
||||
|
||||
if( *t == 0 )
|
||||
break;
|
||||
}
|
||||
tw = min( max( tw, LABEL_MIN_WIDTH ), LABEL_MAX_WIDTH );
|
||||
th = max( th, LABEL_HEIGHT );
|
||||
if( icon != NULL && th < ih )
|
||||
ty += (ih - th) / 2; // vertically center text
|
||||
|
||||
// layout buttons
|
||||
int* bw = new int[buttonCount];
|
||||
int buttonTotalWidth = BUTTON_GAP * (buttonCount - 1);
|
||||
for( int i = 0; i < buttonCount; i++ ) {
|
||||
int w = textLengthAsDLUs( hdc, buttons[i], -1 ) + 16;
|
||||
bw[i] = max( BUTTON_WIDTH, w );
|
||||
buttonTotalWidth += bw[i];
|
||||
}
|
||||
|
||||
// layout dialog
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
int dw = max( tx + tw + INSETS_RIGHT, BUTTON_LEFT_RIGHT_GAP + buttonTotalWidth + BUTTON_LEFT_RIGHT_GAP );
|
||||
int dh = max( iy + ih, ty + th ) + BUTTON_TOP_GAP + BUTTON_HEIGHT + INSETS_BOTTOM;
|
||||
|
||||
// center dialog in owner
|
||||
RECT ownerRect{ 0 };
|
||||
if( ::GetClientRect( owner, &ownerRect ) ) {
|
||||
dx = (pixel2dluX( ownerRect.right - ownerRect.left ) - dw) / 2;
|
||||
dy = (pixel2dluY( ownerRect.bottom - ownerRect.top ) - dh) / 2;
|
||||
}
|
||||
|
||||
// layout button area
|
||||
int bx = dw - buttonTotalWidth - BUTTON_LEFT_RIGHT_GAP;
|
||||
int by = dh - BUTTON_HEIGHT - INSETS_BOTTOM;
|
||||
|
||||
// get font info needed for DS_SETFONT
|
||||
int fontPointSize = (ncMetrics.lfMessageFont.lfHeight < 0)
|
||||
? -MulDiv( ncMetrics.lfMessageFont.lfHeight, 72, ::GetDeviceCaps( hdc, LOGPIXELSY ) )
|
||||
: ncMetrics.lfMessageFont.lfHeight;
|
||||
LPCWSTR fontFaceName = ncMetrics.lfMessageFont.lfFaceName;
|
||||
|
||||
// delete DC and font
|
||||
::DeleteDC( hdc );
|
||||
::DeleteObject( hfont );
|
||||
|
||||
// (approximately) calculate memory size needed for in-memory template
|
||||
int templSize = (sizeof(DLGTEMPLATE) + /*menu*/ 2 + /*class*/ 2 + /*title*/ 2)
|
||||
+ ((sizeof(DLGITEMTEMPLATE) + /*class*/ 4 + /*title/icon*/ 4 + /*creation data*/ 2) * (/*icon+text*/2 + buttonCount))
|
||||
+ (title != NULL ? (wcslen( title ) + 1) * sizeof(wchar_t) : 0)
|
||||
+ /*fontPointSize*/ 2 + ((wcslen( fontFaceName ) + 1) * sizeof(wchar_t))
|
||||
+ ((wcslen( wrappedText ) + 1) * sizeof(wchar_t));
|
||||
for( int i = 0; i < buttonCount; i++ )
|
||||
templSize += ((wcslen( buttons[i] ) + 1) * sizeof(wchar_t));
|
||||
|
||||
templSize += (2 * (1 + 1 + buttonCount)); // necessary for DWORD alignment
|
||||
templSize += 100; // some reserve
|
||||
|
||||
// allocate memory for in-memory template
|
||||
byte* templ = new byte[templSize];
|
||||
if( templ == NULL )
|
||||
return NULL;
|
||||
|
||||
|
||||
//---- define dialog box ----
|
||||
|
||||
LPDLGTEMPLATE lpdt = (LPDLGTEMPLATE) templ;
|
||||
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT;
|
||||
lpdt->cdit = /*text*/ 1 + buttonCount; // number of controls
|
||||
lpdt->x = dx;
|
||||
lpdt->y = dy;
|
||||
lpdt->cx = dw;
|
||||
lpdt->cy = dh;
|
||||
|
||||
LPWORD lpw = (LPWORD) (lpdt + 1);
|
||||
*lpw++ = 0; // no menu
|
||||
*lpw++ = 0; // predefined dialog box class (by default)
|
||||
if( title != NULL ) {
|
||||
wcscpy( (LPWSTR) lpw, title );
|
||||
lpw += wcslen( title ) + 1;
|
||||
} else
|
||||
*lpw++ = 0; // no title
|
||||
|
||||
// for DS_SETFONT
|
||||
*lpw++ = fontPointSize;
|
||||
wcscpy( (LPWSTR) lpw, fontFaceName );
|
||||
lpw += wcslen( fontFaceName ) + 1;
|
||||
|
||||
//---- define icon ----
|
||||
|
||||
if( icon != NULL ) {
|
||||
lpdt->cdit++;
|
||||
|
||||
lpw = lpwAlign( lpw );
|
||||
LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE) lpw;
|
||||
lpdit->x = ix;
|
||||
lpdit->y = iy;
|
||||
lpdit->cx = iw;
|
||||
lpdit->cy = ih;
|
||||
lpdit->id = ID_BUTTON1 - 1;
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | SS_ICON;
|
||||
|
||||
lpw = (LPWORD) (lpdit + 1);
|
||||
*lpw++ = 0xffff; *lpw++ = 0x0082; // Static class
|
||||
*lpw++ = 0xffff; *lpw++ = (WORD) icon; // icon
|
||||
*lpw++ = 0; // creation data
|
||||
}
|
||||
|
||||
|
||||
//---- define text ----
|
||||
|
||||
lpw = lpwAlign( lpw );
|
||||
LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE) lpw;
|
||||
lpdit->x = tx;
|
||||
lpdit->y = ty;
|
||||
lpdit->cx = tw;
|
||||
lpdit->cy = th;
|
||||
lpdit->id = ID_BUTTON1 - 2;
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL;
|
||||
|
||||
lpw = (LPWORD) (lpdit + 1);
|
||||
*lpw++ = 0xffff; *lpw++ = 0x0082; // Static class
|
||||
wcscpy( (LPWSTR) lpw, wrappedText ); lpw += wcslen( wrappedText ) + 1; // text
|
||||
*lpw++ = 0; // creation data
|
||||
|
||||
|
||||
//---- define buttons ----
|
||||
|
||||
defaultButton = min( max( defaultButton, 0 ), buttonCount - 1 );
|
||||
int buttonId = ID_BUTTON1;
|
||||
for( int i = 0; i < buttonCount; i++ ) {
|
||||
lpw = lpwAlign( lpw );
|
||||
LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE) lpw;
|
||||
lpdit->x = bx;
|
||||
lpdit->y = by;
|
||||
lpdit->cx = bw[i];
|
||||
lpdit->cy = BUTTON_HEIGHT;
|
||||
lpdit->id = buttonId++;
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | (i == 0 ? WS_GROUP : 0)
|
||||
| BS_TEXT | (i == defaultButton ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON);
|
||||
|
||||
lpw = (LPWORD) (lpdit + 1);
|
||||
*lpw++ = 0xffff; *lpw++ = 0x0080; // Button class
|
||||
wcscpy( (LPWSTR) lpw, buttons[i] ); lpw += wcslen( buttons[i] ) + 1; // text
|
||||
*lpw++ = 0; // creation data
|
||||
|
||||
bx += bw[i] + BUTTON_GAP;
|
||||
}
|
||||
|
||||
delete[] wrappedText;
|
||||
delete[] bw;
|
||||
|
||||
return templ;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK focusDefaultButtonProc( HWND hwnd, LPARAM lParam ) {
|
||||
if( ::GetWindowLong( hwnd, GWL_ID ) >= ID_BUTTON1 ) {
|
||||
LONG style = ::GetWindowLong( hwnd, GWL_STYLE );
|
||||
if( (style & BS_DEFPUSHBUTTON) != 0 ) {
|
||||
::SetFocus( hwnd );
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK messageDialogProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
|
||||
switch( uMsg ) {
|
||||
case WM_INITDIALOG:
|
||||
::EnumChildWindows( hwnd, focusDefaultButtonProc, 0 );
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
::EndDialog( hwnd, wParam );
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int textLengthAsDLUs( HDC hdc, LPCWSTR str, int strLen ) {
|
||||
SIZE size{ 0 };
|
||||
::GetTextExtentPoint32( hdc, str, (strLen >= 0) ? strLen : wcslen( str ), &size );
|
||||
return pixel2dluX( size.cx );
|
||||
}
|
||||
|
||||
static LONG pixel2dluX( LONG px ) {
|
||||
return MulDiv( px, 4, LOWORD( ::GetDialogBaseUnits() ) );
|
||||
}
|
||||
|
||||
static LONG pixel2dluY( LONG py ) {
|
||||
return MulDiv( py, 8, HIWORD( ::GetDialogBaseUnits() ) );
|
||||
}
|
||||
|
||||
static LONG dluX2pixel( LONG dluX ) {
|
||||
return MulDiv( dluX, LOWORD( ::GetDialogBaseUnits() ), 4 );
|
||||
}
|
||||
|
||||
static LPWORD lpwAlign( LPWORD lpIn ) {
|
||||
ULONG_PTR ul = (ULONG_PTR) lpIn;
|
||||
ul += 3;
|
||||
ul >>= 2;
|
||||
ul <<= 2;
|
||||
return (LPWORD) ul;
|
||||
}
|
||||
@@ -25,8 +25,8 @@
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
// see FlatWndProc.cpp
|
||||
HWND getWindowHandle( JNIEnv* env, jobject window );
|
||||
// declare external methods
|
||||
extern HWND getWindowHandle( JNIEnv* env, jobject window );
|
||||
|
||||
//---- Utility ----------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 <windows.h>
|
||||
#include <jni.h>
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
//---- class AutoReleaseString ------------------------------------------------
|
||||
|
||||
class AutoReleaseString {
|
||||
JNIEnv* env;
|
||||
jstring javaString;
|
||||
const jchar* chars;
|
||||
|
||||
public:
|
||||
AutoReleaseString( JNIEnv* _env, jstring _javaString );
|
||||
~AutoReleaseString();
|
||||
|
||||
operator LPCWSTR() { return (LPCWSTR) chars; }
|
||||
};
|
||||
|
||||
//---- class AutoReleaseStringArray -------------------------------------------
|
||||
|
||||
class AutoReleaseStringArray {
|
||||
JNIEnv* env;
|
||||
jstring* javaStringArray;
|
||||
const jchar** charsArray;
|
||||
|
||||
public:
|
||||
UINT count;
|
||||
|
||||
public:
|
||||
AutoReleaseStringArray( JNIEnv* _env, jobjectArray _javaStringArray );
|
||||
~AutoReleaseStringArray();
|
||||
|
||||
operator LPCWSTR*() { return (LPCWSTR*) charsArray; }
|
||||
};
|
||||
@@ -27,6 +27,52 @@ extern "C" {
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_DWMWA_COLOR_DEFAULT -1L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_DWMWA_COLOR_NONE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_DWMWA_COLOR_NONE -2L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_OVERWRITEPROMPT
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_OVERWRITEPROMPT 2L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_STRICTFILETYPES
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_STRICTFILETYPES 4L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOCHANGEDIR
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOCHANGEDIR 8L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_PICKFOLDERS
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_PICKFOLDERS 32L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FORCEFILESYSTEM
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FORCEFILESYSTEM 64L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_ALLNONSTORAGEITEMS
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_ALLNONSTORAGEITEMS 128L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOVALIDATE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOVALIDATE 256L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_ALLOWMULTISELECT
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_ALLOWMULTISELECT 512L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_PATHMUSTEXIST
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_PATHMUSTEXIST 2048L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FILEMUSTEXIST
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FILEMUSTEXIST 4096L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_CREATEPROMPT
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_CREATEPROMPT 8192L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_SHAREAWARE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_SHAREAWARE 16384L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOREADONLYRETURN
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOREADONLYRETURN 32768L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOTESTFILECREATE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NOTESTFILECREATE 65536L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_HIDEMRUPLACES
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_HIDEMRUPLACES 131072L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_HIDEPINNEDPLACES
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_HIDEPINNEDPLACES 262144L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NODEREFERENCELINKS
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_NODEREFERENCELINKS 1048576L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_OKBUTTONNEEDSINTERACTION
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_OKBUTTONNEEDSINTERACTION 2097152L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_DONTADDTORECENT
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_DONTADDTORECENT 33554432L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FORCESHOWHIDDEN
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FORCESHOWHIDDEN 268435456L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_DEFAULTNOMINIMODE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_DEFAULTNOMINIMODE 536870912L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FORCEPREVIEWPANEON
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_FORCEPREVIEWPANEON 1073741824L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_SUPPORTSTREAMABLEITEMS
|
||||
#define com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_FOS_SUPPORTSTREAMABLEITEMS -2147483648L
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeWindowsLibrary
|
||||
* Method: getOSBuildNumberImpl
|
||||
@@ -67,6 +113,30 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_
|
||||
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_dwmSetWindowAttributeDWORD
|
||||
(JNIEnv *, jclass, jlong, jint, jint);
|
||||
|
||||
/*
|
||||
* 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;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, jobject, jint, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeWindowsLibrary
|
||||
* Method: showMessageDialog
|
||||
* Signature: (JILjava/lang/String;Ljava/lang/String;I[Ljava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showMessageDialog
|
||||
(JNIEnv *, jclass, jlong, jint, jstring, jstring, jint, jobjectArray);
|
||||
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeWindowsLibrary
|
||||
* Method: showMessageBox
|
||||
* Signature: (JLjava/lang/String;Ljava/lang/String;I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showMessageBox
|
||||
(JNIEnv *, jclass, jlong, jstring, jstring, jint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user