System File Chooser: update current filter before invoking approve callback and after closing dialog (issue #1065)

This commit is contained in:
Karl Tauber
2026-01-05 16:54:57 +01:00
parent 7680c3a817
commit edda52048c
19 changed files with 207 additions and 72 deletions

View File

@@ -73,7 +73,8 @@ jobs:
run: ./gradlew build-natives --no-daemon
- name: Sign Windows DLLs
if: matrix.os == 'windows-latest'
if: false
# if: matrix.os == 'windows-latest'
uses: skymatic/code-sign-action@v3
with:
certificate: '${{ secrets.CODE_SIGN_CERT_BASE64 }}'
@@ -82,7 +83,8 @@ jobs:
folder: 'flatlaf-core/src/main/resources/com/formdev/flatlaf/natives'
- name: Sign macOS natives
if: matrix.os == 'DISABLED--macos-latest'
if: false
# if: matrix.os == 'DISABLED--macos-latest'
env:
CERT_BASE64: ${{ secrets.CODE_SIGN_CERT_BASE64 }}
CERT_PASSWORD: ${{ secrets.CODE_SIGN_CERT_PASSWORD }}

View File

@@ -3,6 +3,8 @@ FlatLaf Change Log
## 3.7.1-SNAPSHOT
- System File Chooser: Update current filter before invoking approve callback
and after closing dialog. (issue #1065)
- ComboBox: Added UI property `ComboBox.buttonFocusedEditableBackground`. (issue
#1068)
- Popup: Fixed scrolling popup painting issue on Windows 10 when a glass pane is

View File

@@ -37,7 +37,7 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class FlatNativeLinuxLibrary
{
private static int API_VERSION_LINUX = 3003;
private static int API_VERSION_LINUX = 3004;
/**
* Checks whether native library is loaded/available.
@@ -186,8 +186,8 @@ public class FlatNativeLinuxLibrary
* Use '__' for '_' character (e.g. "Choose__and__Quit").
* @param currentName user-editable filename currently shown in the filename field in save dialog; or {@code null}
* @param currentFolder current directory shown in the dialog; or {@code null}
* @param optionsSet options to set; see {@code FOS_*} constants
* @param optionsClear options to clear; see {@code FOS_*} constants
* @param optionsSet options to set; see {@code FC_*} constants
* @param optionsClear options to clear; see {@code FC_*} constants
* @param callback approve callback; or {@code null}
* @param fileTypeIndex the file type that appears as selected (zero-based)
* @param fileTypes file types that the dialog can open or save.
@@ -195,19 +195,20 @@ public class FlatNativeLinuxLibrary
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
* Subsequent strings are the filter patterns (e.g. "*.txt" or "*").
* {@code null} is required to mark end of filter.
* @param retFileTypeIndex returns selected file type (zero-based); array must be have one element
* @return file path(s) that the user selected; an empty array if canceled;
* or {@code null} on failures (no dialog shown)
*
* @since 3.7
* @since 3.7.1
*/
public native static String[] showFileChooser( Window owner, int dark, boolean open,
String title, String okButtonLabel, String currentName, String currentFolder,
int optionsSet, int optionsClear, FileChooserCallback callback,
int fileTypeIndex, String... fileTypes );
int fileTypeIndex, String[] fileTypes, int[] retFileTypeIndex );
/** @since 3.7 */
/** @since 3.7.1 */
public interface FileChooserCallback {
boolean approve( String[] files, long hwndFileDialog );
boolean approve( String[] files, int fileTypeIndex, long hwndFileDialog );
}
/**

View File

@@ -45,7 +45,7 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class FlatNativeMacLibrary
{
private static int API_VERSION_MACOS = 2002;
private static int API_VERSION_MACOS = 2003;
/**
* Checks whether native library is loaded/available.
@@ -117,20 +117,21 @@ public class FlatNativeMacLibrary
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
* Subsequent strings are the filter patterns (e.g. "txt" or "*").
* {@code null} is required to mark end of filter.
* @param retFileTypeIndex returns selected file type (zero-based); array must be have one element
* @return file path(s) that the user selected; an empty array if canceled;
* or {@code null} on failures (no dialog shown)
*
* @since 3.7
* @since 3.7.1
*/
public native static String[] showFileChooser( Window owner, int dark, boolean open,
String title, String prompt, String message, String filterFieldLabel,
String nameFieldLabel, String nameFieldStringValue, String directoryURL,
int optionsSet, int optionsClear, FileChooserCallback callback,
int fileTypeIndex, String... fileTypes );
int fileTypeIndex, String[] fileTypes, int[] retFileTypeIndex );
/** @since 3.7 */
/** @since 3.7.1 */
public interface FileChooserCallback {
boolean approve( String[] files, long hwndFileDialog );
boolean approve( String[] files, int fileTypeIndex, long hwndFileDialog );
}
/**

View File

@@ -31,7 +31,7 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class FlatNativeWindowsLibrary
{
private static int API_VERSION_WINDOWS = 1002;
private static int API_VERSION_WINDOWS = 1003;
private static long osBuildNumber = Long.MIN_VALUE;
@@ -226,20 +226,21 @@ public class FlatNativeWindowsLibrary
* Pairs of strings are required for each filter.
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
* Second string is the filter pattern (e.g. "*.txt", "*.exe;*.dll" or "*.*").
* @param retFileTypeIndex returns selected file type (zero-based); array must be have one element
* @return file path(s) that the user selected; an empty array if canceled;
* or {@code null} on failures (no dialog shown)
*
* @since 3.7
* @since 3.7.1
*/
public native static String[] showFileChooser( Window owner, boolean open,
String title, String okButtonLabel, String fileNameLabel, String fileName,
String folder, String saveAsItem, String defaultFolder, String defaultExtension,
int optionsSet, int optionsClear, FileChooserCallback callback,
int fileTypeIndex, String... fileTypes );
int fileTypeIndex, String[] fileTypes, int[] retFileTypeIndex );
/** @since 3.7 */
/** @since 3.7.1 */
public interface FileChooserCallback {
boolean approve( String[] files, long hwndFileDialog );
boolean approve( String[] files, int fileTypeIndex, long hwndFileDialog );
}
/**

View File

@@ -28,6 +28,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -607,6 +608,11 @@ public class SystemFileChooser
return filters2;
}
private void updateFileFilter( List<FileFilter> filters, int index ) {
if( index >= 0 && index < filters.size() )
setFileFilter( filters.get( index ) );
}
public ApproveCallback getApproveCallback() {
return approveCallback;
}
@@ -890,6 +896,7 @@ public class SystemFileChooser
// filter
int fileTypeIndex = 0;
ArrayList<String> fileTypes = new ArrayList<>();
ArrayList<FileFilter> fileTypeFilters = new ArrayList<>();
if( !fc.isDirectorySelectionEnabled() ) {
List<FileFilter> filters = fc.getFiltersForDialog();
if( !filters.isEmpty() ) {
@@ -898,9 +905,11 @@ public class SystemFileChooser
if( filter instanceof FileNameExtensionFilter ) {
fileTypes.add( filter.getDescription() );
fileTypes.add( "*." + String.join( ";*.", ((FileNameExtensionFilter)filter).getExtensions() ) );
fileTypeFilters.add( filter );
} else if( filter instanceof AcceptAllFileFilter ) {
fileTypes.add( filter.getDescription() );
fileTypes.add( "*.*" );
fileTypeFilters.add( filter );
}
}
}
@@ -916,19 +925,24 @@ public class SystemFileChooser
// callback
FlatNativeWindowsLibrary.FileChooserCallback callback = (fc.getApproveCallback() != null)
? (files, hwndFileDialog) -> {
? (files, fileTypeIndex2, hwndFileDialog) -> {
fc.updateFileFilter( fileTypeFilters, fileTypeIndex2 );
return invokeApproveCallback( fc, files, new WindowsApproveContext( hwndFileDialog ) );
} : null;
// show system file dialog
return FlatNativeWindowsLibrary.showFileChooser( owner, open,
int[] retFileTypeIndex = { -1 };
String[] result = FlatNativeWindowsLibrary.showFileChooser( owner, open,
fc.getDialogTitle(), approveButtonText,
fc.getPlatformProperty( WINDOWS_FILE_NAME_LABEL ),
fileName, folder, saveAsItem,
fc.getPlatformProperty( WINDOWS_DEFAULT_FOLDER ),
fc.getPlatformProperty( WINDOWS_DEFAULT_EXTENSION ),
optionsSet, optionsClear, callback,
fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ) );
fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ), retFileTypeIndex );
if( result != null )
fc.updateFileFilter( fileTypeFilters, retFileTypeIndex[0] );
return result;
}
//---- class WindowsApproveContext ----
@@ -1013,6 +1027,7 @@ public class SystemFileChooser
// filter
int fileTypeIndex = 0;
ArrayList<String> fileTypes = new ArrayList<>();
ArrayList<FileFilter> fileTypeFilters = new ArrayList<>();
if( !fc.isDirectorySelectionEnabled() ) {
List<FileFilter> filters = fc.getFiltersForDialog();
if( !filters.isEmpty() ) {
@@ -1023,10 +1038,12 @@ public class SystemFileChooser
for( String ext : ((FileNameExtensionFilter)filter).getExtensions() )
fileTypes.add( ext );
fileTypes.add( null );
fileTypeFilters.add( filter );
} else if( filter instanceof AcceptAllFileFilter ) {
fileTypes.add( filter.getDescription() );
fileTypes.add( "*" );
fileTypes.add( null );
fileTypeFilters.add( filter );
}
}
}
@@ -1034,18 +1051,23 @@ public class SystemFileChooser
// callback
FlatNativeMacLibrary.FileChooserCallback callback = (fc.getApproveCallback() != null)
? (files, hwndFileDialog) -> {
? (files, fileTypeIndex2, hwndFileDialog) -> {
fc.updateFileFilter( fileTypeFilters, fileTypeIndex2 );
return invokeApproveCallback( fc, files, new MacApproveContext( hwndFileDialog ) );
} : null;
// show system file dialog
return FlatNativeMacLibrary.showFileChooser( owner, dark, open,
int[] retFileTypeIndex = { -1 };
String[] result = FlatNativeMacLibrary.showFileChooser( owner, dark, open,
fc.getDialogTitle(), fc.getApproveButtonText(),
fc.getPlatformProperty( MAC_MESSAGE ),
fc.getPlatformProperty( MAC_FILTER_FIELD_LABEL ),
fc.getPlatformProperty( MAC_NAME_FIELD_LABEL ),
nameFieldStringValue, directoryURL, optionsSet, optionsClear, callback,
fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ) );
fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ), retFileTypeIndex );
if( result != null )
fc.updateFileFilter( fileTypeFilters, retFileTypeIndex[0] );
return result;
}
//---- class MacApproveContext ----
@@ -1141,6 +1163,7 @@ public class SystemFileChooser
// filter
int fileTypeIndex = 0;
ArrayList<String> fileTypes = new ArrayList<>();
ArrayList<FileFilter> fileTypeFilters = new ArrayList<>();
if( !fc.isDirectorySelectionEnabled() ) {
List<FileFilter> filters = fc.getFiltersForDialog();
if( !filters.isEmpty() ) {
@@ -1151,10 +1174,12 @@ public class SystemFileChooser
for( String ext : ((FileNameExtensionFilter)filter).getExtensions() )
fileTypes.add( caseInsensitiveGlobPattern( ext ) );
fileTypes.add( null );
fileTypeFilters.add( filter );
} else if( filter instanceof AcceptAllFileFilter ) {
fileTypes.add( filter.getDescription() );
fileTypes.add( "*" );
fileTypes.add( null );
fileTypeFilters.add( filter );
}
}
}
@@ -1162,15 +1187,20 @@ public class SystemFileChooser
// callback
FlatNativeLinuxLibrary.FileChooserCallback callback = (fc.getApproveCallback() != null)
? (files, hwndFileDialog) -> {
? (files, fileTypeIndex2, hwndFileDialog) -> {
fc.updateFileFilter( fileTypeFilters, fileTypeIndex2 );
return invokeApproveCallback( fc, files, new LinuxApproveContext( hwndFileDialog ) );
} : null;
// show system file dialog
return FlatNativeLinuxLibrary.showFileChooser( owner, dark, open,
int[] retFileTypeIndex = { -1 };
String[] result = FlatNativeLinuxLibrary.showFileChooser( owner, dark, open,
fc.getDialogTitle(), approveButtonText, currentName, currentFolder,
optionsSet, optionsClear, callback,
fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ) );
fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ), retFileTypeIndex );
if( result != null )
fc.updateFileFilter( fileTypeFilters, retFileTypeIndex[0] );
return result;
}
private String caseInsensitiveGlobPattern( String ext ) {
@@ -1253,6 +1283,8 @@ public class SystemFileChooser
{
@Override
public File[] showDialog( Window owner, SystemFileChooser fc ) {
IdentityHashMap<javax.swing.filechooser.FileFilter, FileFilter> filterMap = new IdentityHashMap<>();
JFileChooser chooser = new JFileChooser() {
@Override
public void approveSelection() {
@@ -1273,6 +1305,7 @@ public class SystemFileChooser
// callback
ApproveCallback approveCallback = fc.getApproveCallback();
if( approveCallback != null ) {
updateFileFilter( fc, this, filterMap );
int result = approveCallback.approve( files, new SwingApproveContext( this ) );
if( result == CANCEL_OPTION )
return;
@@ -1313,6 +1346,7 @@ public class SystemFileChooser
chooser.addChoosableFileFilter( jfilter );
if( filter == currentFilter )
chooser.setFileFilter( jfilter );
filterMap.put( jfilter, filter );
}
}
}
@@ -1340,6 +1374,7 @@ public class SystemFileChooser
// show dialog
int result = chooser.showDialog( owner, null );
updateFileFilter( fc, chooser, filterMap );
// save window size
Dimension windowSize = chooser.getSize();
@@ -1367,6 +1402,14 @@ public class SystemFileChooser
return null;
}
private void updateFileFilter( SystemFileChooser fc, JFileChooser chooser,
IdentityHashMap<javax.swing.filechooser.FileFilter, FileFilter> filterMap )
{
FileFilter fileFilter = filterMap.get( chooser.getFileFilter() );
if( fileFilter != null )
fc.setFileFilter( fileFilter );
}
private static boolean checkMustExist( JFileChooser chooser, File[] files ) {
for( File file : files ) {
if( !file.exists() ) {

View File

@@ -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 3003
#define API_VERSION_LINUX 3004
//---- JNI methods ------------------------------------------------------------

View File

@@ -58,6 +58,7 @@ static void initFilters( GtkFileChooser* chooser, JNIEnv* env, jint fileTypeInde
gtk_file_chooser_add_filter( chooser, filter );
if( fileTypeIndex == filterIndex )
gtk_file_chooser_set_filter( chooser, filter );
g_object_set_data( G_OBJECT( filter ), "flatlaf-filter-index", GINT_TO_POINTER( filterIndex + 1 ) );
filter = NULL;
filterIndex++;
}
@@ -130,32 +131,42 @@ struct ResponseData {
JNIEnv* env;
jobject callback;
GSList* fileList;
int filterIndex;
ResponseData( JNIEnv* _env, jobject _callback ) {
env = _env;
callback = _callback;
fileList = NULL;
filterIndex = -1;
}
};
static void handle_response( GtkWidget* dialog, gint responseId, gpointer data ) {
GtkFileChooser* chooser = GTK_FILE_CHOOSER( dialog );
ResponseData *response = static_cast<ResponseData*>( data );
// get selected filter (even if user cancels dialog)
GtkFileFilter* filter = gtk_file_chooser_get_filter( chooser );
response->filterIndex = (filter != NULL)
? GPOINTER_TO_INT( g_object_get_data( G_OBJECT( filter ), "flatlaf-filter-index" ) ) - 1
: -1;
// 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 ) );
GSList* fileList = gtk_file_chooser_get_filenames( chooser );
jobjectArray files = fileListToStringArray( response->env, fileList );
jint filterIndex = response->filterIndex;
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 ) )
jmethodID approveID = response->env->GetMethodID( cls, "approve", "([Ljava/lang/String;IJ)Z" );
if( approveID != NULL && !response->env->CallBooleanMethod( response->callback, approveID, files, filterIndex, window ) )
return; // keep dialog open
}
response->fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER( dialog ) );
response->fileList = gtk_file_chooser_get_filenames( chooser );
}
// hide/destroy file dialog and quit loop
@@ -170,7 +181,8 @@ extern "C"
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
( JNIEnv* env, jclass cls, jobject owner, jint dark, jboolean open,
jstring title, jstring okButtonLabel, jstring currentName, jstring currentFolder,
jint optionsSet, jint optionsClear, jobject callback, jint fileTypeIndex, jobjectArray fileTypes )
jint optionsSet, jint optionsClear, jobject callback,
jint fileTypeIndex, jobjectArray fileTypes, jintArray retFileTypeIndex )
{
// initialize GTK
if( !gtk_init_check( NULL, NULL ) )
@@ -282,7 +294,11 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrar
// start event loop (will be quit in respone handler)
gtk_main();
// canceled?
// return selected filter
jint selectedFilterIndex = responseData.filterIndex;
env->SetIntArrayRegion( retFileTypeIndex, 0, 1, &selectedFilterIndex );
// return empty array if canceled
if( responseData.fileList == NULL )
return newJavaStringArray( env, 0 );

View File

@@ -64,10 +64,10 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_is
/*
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary
* Method: showFileChooser
* Signature: (Ljava/awt/Window;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeLinuxLibrary/FileChooserCallback;I[Ljava/lang/String;)[Ljava/lang/String;
* Signature: (Ljava/awt/Window;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeLinuxLibrary/FileChooserCallback;I[Ljava/lang/String;[I)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
(JNIEnv *, jclass, jobject, jint, jboolean, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray);
(JNIEnv *, jclass, jobject, jint, jboolean, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray, jintArray);
/*
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary

View File

@@ -82,10 +82,10 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_togg
/*
* 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;
* 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;[I)[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);
(JNIEnv *, jclass, jobject, jint, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray, jintArray);
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary

View File

@@ -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 2002
#define API_VERSION_MACOS 2003
//---- JNI methods ------------------------------------------------------------

View File

@@ -35,6 +35,7 @@ static NSArray* getDialogURLs( NSSavePanel* dialog );
@interface FileChooserDelegate : NSObject <NSOpenSavePanelDelegate, NSWindowDelegate> {
NSArray* _filters;
int _selectedFormatIndex;
JavaVM* _jvm;
jobject _callback;
@@ -43,18 +44,26 @@ static NSArray* getDialogURLs( NSSavePanel* dialog );
@property (nonatomic, assign) NSSavePanel* dialog;
- (id) init;
- (void) initFilterAccessoryView: (NSMutableArray*)filters :(int)filterIndex
:(NSString*)filterFieldLabel :(bool)showSingleFilterField;
- (void) selectFormat: (id)sender;
- (void) selectFormatAtIndex: (int)index;
- (int) selectedFormatIndex;
@end
@implementation FileChooserDelegate
- (id) init {
_selectedFormatIndex = -1;
return self;
}
- (void) initFilterAccessoryView: (NSMutableArray*)filters :(int)filterIndex
:(NSString*)filterFieldLabel :(bool)showSingleFilterField
{
_filters = filters;
_selectedFormatIndex = filterIndex;
// get filter names
NSArray* filterNames = filters.lastObject;
@@ -125,6 +134,12 @@ static NSArray* getDialogURLs( NSSavePanel* dialog );
// 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;
_selectedFormatIndex = index;
}
- (int) selectedFormatIndex {
return _selectedFormatIndex;
}
//---- NSOpenSavePanelDelegate ----
@@ -161,12 +176,13 @@ static NSArray* getDialogURLs( NSSavePanel* dialog );
JNI_THREAD_ENTER( _jvm, true )
jobjectArray files = urlsToStringArray( env, urls );
jint selectedFormatIndex = ((FileChooserDelegate*)((NSSavePanel*)sender).delegate).selectedFormatIndex;
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 ) ) {
jmethodID approveID = env->GetMethodID( cls, "approve", "([Ljava/lang/String;IJ)Z" );
if( approveID != NULL && !env->CallBooleanMethod( _callback, approveID, files, selectedFormatIndex, window ) ) {
_urlsSet = NULL;
return false; // keep dialog open
}
@@ -265,7 +281,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_
( 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 )
jint optionsSet, jint optionsClear, jobject callback,
jint fileTypeIndex, jobjectArray fileTypes, jintArray retFileTypeIndex )
{
JNI_COCOA_ENTER()
@@ -365,17 +382,26 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_
// show dialog
NSModalResponse response = [dialog runModal];
// return selected filter
jint selectedFormatIndex = delegate.selectedFormatIndex;
env->SetIntArrayRegion( retFileTypeIndex, 0, 1, &selectedFormatIndex );
[delegate release];
// return empty array if canceled
if( response != NSModalResponseOK ) {
*purls = @[];
return;
}
// get selected file(s)
*purls = getDialogURLs( dialog );
JNI_COCOA_CATCH()
}];
// return null on failures
if( urls == NULL )
return NULL;

View File

@@ -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 1002
#define API_VERSION_WINDOWS 1003
//---- JNI methods ------------------------------------------------------------

View File

@@ -128,6 +128,11 @@ public:
} else
files = getFiles( env, false, dialog );
// get selected filter
UINT selectedFileTypeIndex = 0;
dialog->GetFileTypeIndex( &selectedFileTypeIndex );
jint jselectedFileTypeIndex = selectedFileTypeIndex - 1;
// get hwnd of file dialog
HWND hwndFileDialog = 0;
AutoReleasePtr<IOleWindow> window;
@@ -136,10 +141,10 @@ public:
// invoke callback: boolean approve( String[] files, long hwnd );
jclass cls = env->GetObjectClass( callback );
jmethodID approveID = env->GetMethodID( cls, "approve", "([Ljava/lang/String;J)Z" );
jmethodID approveID = env->GetMethodID( cls, "approve", "([Ljava/lang/String;IJ)Z" );
if( approveID == NULL )
return S_OK;
return env->CallBooleanMethod( callback, approveID, files, hwndFileDialog ) ? S_OK : S_FALSE;
return env->CallBooleanMethod( callback, approveID, files, jselectedFileTypeIndex, hwndFileDialog ) ? S_OK : S_FALSE;
}
IFACEMETHODIMP OnFolderChange( IFileDialog* ) { return S_OK; }
@@ -147,7 +152,7 @@ public:
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 OnTypeChange( IFileDialog* ) { return S_OK; }
IFACEMETHODIMP OnOverwrite( IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE* ) { return S_OK; }
//---- IUnknown methods ----
@@ -213,7 +218,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr
( JNIEnv* env, jclass cls, jobject owner, jboolean open,
jstring title, jstring okButtonLabel, jstring fileNameLabel, jstring fileName,
jstring folder, jstring saveAsItem, jstring defaultFolder, jstring defaultExtension,
jint optionsSet, jint optionsClear, jobject callback, jint fileTypeIndex, jobjectArray fileTypes )
jint optionsSet, jint optionsClear, jobject callback,
jint fileTypeIndex, jobjectArray fileTypes, jintArray retFileTypeIndex )
{
// initialize COM library
CoInitializer coInitializer;
@@ -285,6 +291,14 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr
HWND hwndOwner = (owner != NULL) ? getWindowHandle( env, owner ) : NULL;
HRESULT hr = dialog->Show( hwndOwner );
dialog->Unadvise( dwCookie );
// return selected filter
UINT selectedFileTypeIndex = 0;
CHECK_HRESULT( dialog->GetFileTypeIndex( &selectedFileTypeIndex ) );
jint jselectedFileTypeIndex = selectedFileTypeIndex - 1;
env->SetIntArrayRegion( retFileTypeIndex, 0, 1, &jselectedFileTypeIndex );
// return empty array if canceled
if( hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
return newJavaStringArray( env, 0 );
CHECK_HRESULT( hr );

View File

@@ -116,10 +116,10 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_
/*
* Class: com_formdev_flatlaf_ui_FlatNativeWindowsLibrary
* Method: showFileChooser
* Signature: (Ljava/awt/Window;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeWindowsLibrary/FileChooserCallback;I[Ljava/lang/String;)[Ljava/lang/String;
* Signature: (Ljava/awt/Window;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeWindowsLibrary/FileChooserCallback;I[Ljava/lang/String;[I)[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);
(JNIEnv *, jclass, jobject, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray, jintArray);
/*
* Class: com_formdev_flatlaf_ui_FlatNativeWindowsLibrary

View File

@@ -110,8 +110,8 @@ public class FlatSystemFileChooserLinuxTest
}
int fileTypeIndex = fileTypeIndexSlider.getValue();
FlatNativeLinuxLibrary.FileChooserCallback callback = (files, hwndFileDialog) -> {
System.out.println( " -- callback " + hwndFileDialog + " " + Arrays.toString( files ) );
FlatNativeLinuxLibrary.FileChooserCallback callback = (files, fileTypeIndex2, hwndFileDialog) -> {
System.out.println( " -- callback " + hwndFileDialog + " " + Arrays.toString( files ) + " " + fileTypeIndex2 );
if( showMessageDialogOnOKCheckBox.isSelected() ) {
System.out.println( FlatNativeLinuxLibrary.showMessageDialog( hwndFileDialog,
JOptionPane.INFORMATION_MESSAGE,
@@ -124,24 +124,28 @@ public class FlatSystemFileChooserLinuxTest
int dark = FlatLaf.isLafDark() ? 1 : 0;
if( direct ) {
int[] retFileTypeIndex = { -1 };
String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, dark, open,
title, okButtonLabel, currentName, currentFolder,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes );
optionsSet.get(), optionsClear.get(), callback,
fileTypeIndex, fileTypes, retFileTypeIndex );
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
outputResult( files, retFileTypeIndex[0] );
} else {
SecondaryLoop secondaryLoop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
String[] fileTypes2 = fileTypes;
new Thread( () -> {
int[] retFileTypeIndex = { -1 };
String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, dark, open,
title, okButtonLabel, currentName, currentFolder,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes2 );
optionsSet.get(), optionsClear.get(), callback,
fileTypeIndex, fileTypes2, retFileTypeIndex );
System.out.println( " secondaryLoop.exit() returned " + secondaryLoop.exit() );
EventQueue.invokeLater( () -> {
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
outputResult( files, retFileTypeIndex[0] );
} );
} ).start();
@@ -150,6 +154,11 @@ public class FlatSystemFileChooserLinuxTest
}
}
private void outputResult( String[] files, int retFileTypeIndex ) {
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
filesField.append( "\n\nretFileTypeIndex " + retFileTypeIndex );
}
private static String n( String s ) {
return s != null && !s.isEmpty() ? s : null;
}

View File

@@ -145,8 +145,8 @@ public class FlatSystemFileChooserMacTest
}
int fileTypeIndex = fileTypeIndexSlider.getValue();
FlatNativeMacLibrary.FileChooserCallback callback = (files, hwndFileDialog) -> {
System.out.println( " -- callback " + hwndFileDialog + " " + Arrays.toString( files ) );
FlatNativeMacLibrary.FileChooserCallback callback = (files, fileTypeIndex2, hwndFileDialog) -> {
System.out.println( " -- callback " + hwndFileDialog + " " + Arrays.toString( files ) + " " + fileTypeIndex2 );
if( showMessageDialogOnOKCheckBox.isSelected() ) {
int result = FlatNativeMacLibrary.showMessageDialog( hwndFileDialog,
JOptionPane.INFORMATION_MESSAGE,
@@ -160,26 +160,30 @@ public class FlatSystemFileChooserMacTest
int dark = FlatLaf.isLafDark() ? 1 : 0;
if( direct ) {
int[] retFileTypeIndex = { -1 };
String[] files = FlatNativeMacLibrary.showFileChooser( owner, dark, open,
title, prompt, message, filterFieldLabel,
nameFieldLabel, nameFieldStringValue, directoryURL,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes );
optionsSet.get(), optionsClear.get(), callback,
fileTypeIndex, fileTypes, retFileTypeIndex );
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
outputResult( files, retFileTypeIndex[0] );
} else {
SecondaryLoop secondaryLoop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
String[] fileTypes2 = fileTypes;
new Thread( () -> {
int[] retFileTypeIndex = { -1 };
String[] files = FlatNativeMacLibrary.showFileChooser( owner, dark, open,
title, prompt, message, filterFieldLabel,
nameFieldLabel, nameFieldStringValue, directoryURL,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes2 );
optionsSet.get(), optionsClear.get(), callback,
fileTypeIndex, fileTypes2, retFileTypeIndex );
System.out.println( " secondaryLoop.exit() returned " + secondaryLoop.exit() );
SwingUtilities.invokeLater( () -> {
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
outputResult( files, retFileTypeIndex[0] );
} );
} ).start();
@@ -188,6 +192,11 @@ public class FlatSystemFileChooserMacTest
}
}
private void outputResult( String[] files, int retFileTypeIndex ) {
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
filesField.append( "\n\nretFileTypeIndex " + retFileTypeIndex );
}
private static String n( String s ) {
return s != null && !s.isEmpty() ? s : null;
}

View File

@@ -310,7 +310,8 @@ public class FlatSystemFileChooserTest
"result", result,
"currentDirectory", fc.getCurrentDirectory(),
"selectedFile", fc.getSelectedFile(),
"selectedFiles", fc.getSelectedFiles() );
"selectedFiles", fc.getSelectedFiles(),
"fileFilter", fc.getFileFilter() );
}
private void outputSwingFileChooser( String type, JFileChooser fc, int result ) {
@@ -319,7 +320,8 @@ public class FlatSystemFileChooserTest
"result", result,
"currentDirectory", fc.getCurrentDirectory(),
"selectedFile", fc.getSelectedFile(),
"selectedFiles", fc.getSelectedFiles() );
"selectedFiles", fc.getSelectedFiles(),
"fileFilter", fc.getFileFilter() );
}
private void outputAWTFileChooser( FileDialog fc ) {

View File

@@ -131,8 +131,8 @@ public class FlatSystemFileChooserWindowsTest
fileTypes = fileTypesStr.trim().split( "[,]+" );
int fileTypeIndex = fileTypeIndexSlider.getValue();
FlatNativeWindowsLibrary.FileChooserCallback callback = (files, hwndFileDialog) -> {
System.out.println( " -- callback " + hwndFileDialog + " " + Arrays.toString( files ) );
FlatNativeWindowsLibrary.FileChooserCallback callback = (files, fileTypeIndex2, hwndFileDialog) -> {
System.out.println( " -- callback " + hwndFileDialog + " " + Arrays.toString( files ) + " " + fileTypeIndex2 );
if( showMessageDialogOnOKCheckBox.isSelected() ) {
System.out.println( FlatNativeWindowsLibrary.showMessageDialog( hwndFileDialog,
JOptionPane.INFORMATION_MESSAGE,
@@ -142,26 +142,30 @@ public class FlatSystemFileChooserWindowsTest
};
if( direct ) {
int[] retFileTypeIndex = { -1 };
String[] files = FlatNativeWindowsLibrary.showFileChooser( owner, open,
title, okButtonLabel, fileNameLabel, fileName,
folder, saveAsItem, defaultFolder, defaultExtension,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes );
optionsSet.get(), optionsClear.get(), callback,
fileTypeIndex, fileTypes, retFileTypeIndex );
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
outputResult( files, retFileTypeIndex[0] );
} else {
SecondaryLoop secondaryLoop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
String[] fileTypes2 = fileTypes;
new Thread( () -> {
int[] retFileTypeIndex = { -1 };
String[] files = FlatNativeWindowsLibrary.showFileChooser( owner, open,
title, okButtonLabel, fileNameLabel, fileName,
folder, saveAsItem, defaultFolder, defaultExtension,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes2 );
optionsSet.get(), optionsClear.get(), callback,
fileTypeIndex, fileTypes2, retFileTypeIndex );
System.out.println( " secondaryLoop.exit() returned " + secondaryLoop.exit() );
EventQueue.invokeLater( () -> {
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
outputResult( files, retFileTypeIndex[0] );
} );
} ).start();
@@ -170,6 +174,11 @@ public class FlatSystemFileChooserWindowsTest
}
}
private void outputResult( String[] files, int retFileTypeIndex ) {
filesField.setText( (files != null) ? Arrays.toString( files ).replace( ',', '\n' ) : "null" );
filesField.append( "\n\nretFileTypeIndex " + retFileTypeIndex );
}
private static String n( String s ) {
return s != null && !s.isEmpty() ? s : null;
}