mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 05:50:53 +03:00
System File Chooser: implemented native bindings for IFileOpenDialog and IFileSaveDialog on Windows
This commit is contained in:
@@ -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", "/NODEFAULTLIB" )
|
||||
else -> emptyList()
|
||||
}
|
||||
} )
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
// increase this version if changing API or functionality of native library
|
||||
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeWindowsLibrary
|
||||
#define API_VERSION_WINDOWS 1001
|
||||
#define API_VERSION_WINDOWS 1002
|
||||
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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 "com_formdev_flatlaf_ui_FlatNativeWindowsLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
// see FlatWndProc.cpp
|
||||
HWND getWindowHandle( JNIEnv* env, jobject window );
|
||||
|
||||
//---- class AutoReleasePtr ---------------------------------------------------
|
||||
|
||||
template<class T> class AutoReleasePtr {
|
||||
T* ptr;
|
||||
|
||||
public:
|
||||
AutoReleasePtr() {
|
||||
ptr = NULL;
|
||||
}
|
||||
~AutoReleasePtr() {
|
||||
if( ptr != NULL )
|
||||
ptr->Release();
|
||||
}
|
||||
T** operator&() { return &ptr; }
|
||||
T* operator->() { return ptr; }
|
||||
operator T*() { return ptr; }
|
||||
};
|
||||
|
||||
//---- class AutoReleaseString ------------------------------------------------
|
||||
|
||||
class AutoReleaseString {
|
||||
JNIEnv* env;
|
||||
jstring javaString;
|
||||
const jchar* chars;
|
||||
|
||||
public:
|
||||
AutoReleaseString( JNIEnv* _env, jstring _javaString ) {
|
||||
env = _env;
|
||||
javaString = _javaString;
|
||||
chars = (javaString != NULL) ? env->GetStringChars( javaString, NULL ) : NULL;
|
||||
}
|
||||
~AutoReleaseString() {
|
||||
if( chars != NULL )
|
||||
env->ReleaseStringChars( javaString, chars );
|
||||
}
|
||||
operator LPCWSTR() { return (LPCWSTR) chars; }
|
||||
};
|
||||
|
||||
//---- 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 {
|
||||
JNIEnv* env;
|
||||
jstring* jnames = NULL;
|
||||
jstring* jspecs = NULL;
|
||||
|
||||
public:
|
||||
UINT count = 0;
|
||||
COMDLG_FILTERSPEC* specs = NULL;
|
||||
|
||||
public:
|
||||
FilterSpec( JNIEnv* _env, jobjectArray fileTypes ) {
|
||||
env = _env;
|
||||
count = env->GetArrayLength( fileTypes ) / 2;
|
||||
if( count <= 0 )
|
||||
return;
|
||||
|
||||
specs = new COMDLG_FILTERSPEC[count];
|
||||
jnames = new jstring[count];
|
||||
jspecs = new jstring[count];
|
||||
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
jnames[i] = (jstring) env->GetObjectArrayElement( fileTypes, i * 2 );
|
||||
jspecs[i] = (jstring) env->GetObjectArrayElement( fileTypes, (i * 2) + 1 );
|
||||
specs[i].pszName = (LPCWSTR) env->GetStringChars( jnames[i] , NULL );
|
||||
specs[i].pszSpec = (LPCWSTR) env->GetStringChars( jspecs[i], NULL );
|
||||
}
|
||||
}
|
||||
~FilterSpec() {
|
||||
if( specs == NULL )
|
||||
return;
|
||||
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
env->ReleaseStringChars( jnames[i], (jchar *) specs[i].pszName );
|
||||
env->ReleaseStringChars( jspecs[i], (jchar *) specs[i].pszSpec );
|
||||
env->DeleteLocalRef( jnames[i] );
|
||||
env->DeleteLocalRef( jspecs[i] );
|
||||
}
|
||||
|
||||
delete[] jnames;
|
||||
delete[] jspecs;
|
||||
delete[] specs;
|
||||
}
|
||||
};
|
||||
|
||||
//---- 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 CHECK_HRESULT( code ) { if( (code) != S_OK ) return NULL; }
|
||||
|
||||
jobjectArray newJavaStringArray( JNIEnv* env, jsize count ) {
|
||||
jclass stringClass = env->FindClass( "java/lang/String" );
|
||||
return env->NewObjectArray( count, stringClass, NULL );
|
||||
}
|
||||
|
||||
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, jint fileTypeIndex, jobjectArray fileTypes )
|
||||
{
|
||||
// initialize COM library
|
||||
CoInitializer coInitializer;
|
||||
if( !coInitializer.initialized )
|
||||
return NULL;
|
||||
|
||||
HWND hwndOwner = getWindowHandle( env, owner );
|
||||
|
||||
// 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 ) ) );
|
||||
|
||||
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 ) );
|
||||
|
||||
FILEOPENDIALOGOPTIONS existingOptions;
|
||||
CHECK_HRESULT( dialog->GetOptions( &existingOptions ) );
|
||||
CHECK_HRESULT( dialog->SetOptions ( (existingOptions & ~optionsClear) | optionsSet ) );
|
||||
|
||||
if( specs.count > 0 ) {
|
||||
CHECK_HRESULT( dialog->SetFileTypes( specs.count, specs.specs ) );
|
||||
if( fileTypeIndex > 0 )
|
||||
CHECK_HRESULT( dialog->SetFileTypeIndex( min( fileTypeIndex + 1, specs.count ) ) );
|
||||
}
|
||||
|
||||
// show dialog
|
||||
HRESULT hr = dialog->Show( hwndOwner );
|
||||
if( hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
|
||||
return newJavaStringArray( env, 0 );
|
||||
CHECK_HRESULT( hr );
|
||||
|
||||
// convert URLs to Java string array
|
||||
if( open ) {
|
||||
AutoReleasePtr<IShellItemArray> shellItems;
|
||||
DWORD count;
|
||||
CHECK_HRESULT( ((IFileOpenDialog*)(IFileDialog*)dialog)->GetResults( &shellItems ) );
|
||||
CHECK_HRESULT( shellItems->GetCount( &count ) );
|
||||
|
||||
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, 0, jpath );
|
||||
env->DeleteLocalRef( jpath );
|
||||
}
|
||||
return array;
|
||||
} else {
|
||||
AutoReleasePtr<IShellItem> shellItem;
|
||||
LPWSTR path;
|
||||
CHECK_HRESULT( dialog->GetResult( &shellItem ) );
|
||||
CHECK_HRESULT( shellItem->GetDisplayName( SIGDN_FILESYSPATH, &path ) );
|
||||
|
||||
jstring jpath = newJavaString( env, path );
|
||||
CoTaskMemFree( path );
|
||||
|
||||
jobjectArray array = newJavaStringArray( env, 1 );
|
||||
env->SetObjectArrayElement( array, 0, jpath );
|
||||
env->DeleteLocalRef( jpath );
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -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,14 @@ 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;III[Ljava/lang/String;)[Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_showFileChooser
|
||||
(JNIEnv *, jclass, jobject, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jint, jobjectArray);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user