diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c index e7332024547..1c9ad0e7871 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c @@ -704,6 +704,54 @@ JNIEXPORT jboolean JNICALL GTK4_NATIVE(gtk_1file_1chooser_1set_1file) } #endif +#ifndef NO_gtk_1file_1dialog_1new +JNIEXPORT jlong JNICALL GTK4_NATIVE(gtk_1file_1dialog_1new) + (JNIEnv *env, jclass that) +{ + jlong rc = 0; + GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1new_FUNC); + rc = (jlong)gtk_file_dialog_new(); + GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1new_FUNC); + return rc; +} +#endif + +#ifndef NO_gtk_1file_1dialog_1select_1folder +JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1file_1dialog_1select_1folder) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2, jlong arg3, jlong arg4) +{ + GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1select_1folder_FUNC); + gtk_file_dialog_select_folder((GtkFileDialog *)arg0, (GtkWindow *)arg1, (GCancellable *)arg2, (GAsyncReadyCallback)arg3, (gpointer)arg4); + GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1select_1folder_FUNC); +} +#endif + +#ifndef NO_gtk_1file_1dialog_1select_1folder_1finish +JNIEXPORT jlong JNICALL GTK4_NATIVE(gtk_1file_1dialog_1select_1folder_1finish) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlongArray arg2) +{ + jlong *lparg2=NULL; + jlong rc = 0; + GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1select_1folder_1finish_FUNC); + if (arg2) if ((lparg2 = (*env)->GetLongArrayElements(env, arg2, NULL)) == NULL) goto fail; + rc = (jlong)gtk_file_dialog_select_folder_finish((GtkFileDialog *)arg0, (GAsyncResult *)arg1, (GError *)lparg2); +fail: + if (arg2 && lparg2) (*env)->ReleaseLongArrayElements(env, arg2, lparg2, 0); + GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1select_1folder_1finish_FUNC); + return rc; +} +#endif + +#ifndef NO_gtk_1file_1dialog_1set_1initial_1folder +JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1file_1dialog_1set_1initial_1folder) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1) +{ + GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1set_1initial_1folder_FUNC); + gtk_file_dialog_set_initial_folder((GtkFileDialog *)arg0, (GFile *)arg1); + GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1set_1initial_1folder_FUNC); +} +#endif + #ifndef NO_gtk_1frame_1set_1child JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1frame_1set_1child) (JNIEnv *env, jclass that, jlong arg0, jlong arg1) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h index 6d5b896ecf4..de0d695c1f0 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h @@ -81,6 +81,10 @@ typedef enum { gtk_1file_1chooser_1get_1files_FUNC, gtk_1file_1chooser_1set_1current_1folder_FUNC, gtk_1file_1chooser_1set_1file_FUNC, + gtk_1file_1dialog_1new_FUNC, + gtk_1file_1dialog_1select_1folder_FUNC, + gtk_1file_1dialog_1select_1folder_1finish_FUNC, + gtk_1file_1dialog_1set_1initial_1folder_FUNC, gtk_1frame_1set_1child_FUNC, gtk_1gesture_1click_1new_FUNC, gtk_1gesture_1drag_1new_FUNC, diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java index ef59930c698..1a9fa6dbb9a 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2023 Syntevo and others. + * Copyright (c) 2021, 2024 Syntevo and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -191,7 +191,29 @@ public class GTK4 { * @param error cast=(GError **) */ public static final native boolean gtk_file_chooser_set_file(long chooser, long file, long error); - + + /* GtkFileDialog */ + public static final native long gtk_file_dialog_new(); + /** + * @param self cast=(GtkFileDialog *) + * @param parent cast=(GtkWindow *) + * @param cancellable cast=(GCancellable *) + * @param callback cast=(GAsyncReadyCallback) + * @param user_data cast=(gpointer) + */ + public static final native void gtk_file_dialog_select_folder(long self, long parent, long cancellable, long callback, long user_data); + /** + * @param self cast=(GtkFileDialog *) + * @param result cast=(GAsyncResult *) + * @param error cast=(GError *) + */ + public static final native long gtk_file_dialog_select_folder_finish(long self, long result, long[] error); + /** + * @param self cast=(GtkFileDialog *) + * @param folder cast=(GFile *) + */ + public static final native void gtk_file_dialog_set_initial_folder(long self, long folder); + /* GtkScrolledWindow */ public static final native long gtk_scrolled_window_new(); /** @param scrolled_window cast=(GtkScrolledWindow *) */ diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SyncDialogUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SyncDialogUtil.java index e7596afcf31..5464d37f000 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SyncDialogUtil.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SyncDialogUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Red Hat Inc. and others. + * Copyright (c) 2020, 2024 Red Hat Inc. and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,7 +14,9 @@ package org.eclipse.swt.internal; import java.lang.reflect.*; +import java.util.function.*; +import org.eclipse.swt.*; import org.eclipse.swt.internal.gtk.*; import org.eclipse.swt.widgets.*; @@ -28,6 +30,41 @@ public class SyncDialogUtil { static int responseID; static Callback dialogResponseCallback; + static Function dialogAsyncFinish; + static Long dialogAsyncValue; + + /** + * This method implements the {@code AsyncReadyCallback} mechanism that is + * used in GTK4. Most operations within GTK4 are executed asynchronously, + * where the user is given the option to respond to the completion of such + * an operation via a callback method, in order to e.g. process the result + * or to apply additional cleanup tasks.
+ * When calling this method, the asynchronous operation is initiated via the + * {code asyncOpen} parameter. Callers have to ensure that the callback + * address is used as argument for the {@code AsyncReadyCallback} parameter. + * From within the callback routine, the {@code asyncFinish} function is + * called, receiving the {@code AsyncResult} of the callback as argument and + * returning the {@code long} value of the callback function.
+ * This method blocks until the callback method has been called. It is + * therefore essential that callers use the address of the {@link Callback} + * as address for the {@code AsyncReadyCallback} object. + */ + static public long run(Display display, Consumer asyncOpen, Function asyncFinish) { + initializeResponseCallback(); + + dialogAsyncFinish = asyncFinish; + asyncOpen.accept(dialogResponseCallback.getAddress()); + + while (!display.isDisposed()) { + if (dialogAsyncValue != null) { + break; + } + display.readAndDispatch(); + } + + disposeResponseCallback(); + return dialogAsyncValue; + } /** * A blocking call that waits for the handling of the signal before returning @@ -53,7 +90,7 @@ static public int run(Display display, long handle, boolean isNativeDialog) { } disposeResponseCallback(); - return responseID; + return (int) responseID; } /** @@ -61,13 +98,15 @@ static public int run(Display display, long handle, boolean isNativeDialog) { * This function should be called before connect the dialog to the "response" signal, as this sets up the callback. */ static void initializeResponseCallback() { - dialogResponseCallback = new Callback(SyncDialogUtil.class, "dialogResponseProc", void.class, new Type[] {long.class, int.class, long.class}); + dialogResponseCallback = new Callback(SyncDialogUtil.class, "dialogResponseProc", void.class, new Type[] {long.class, long.class, long.class}); + dialogAsyncValue = null; responseID = -1; } static void disposeResponseCallback() { dialogResponseCallback.dispose(); dialogResponseCallback = null; + dialogAsyncFinish = null; } /** @@ -77,7 +116,10 @@ static void disposeResponseCallback() { * * Note: Native dialogs are platform dialogs that don't use GtkDialog or GtkWindow. */ - static void dialogResponseProc(long dialog, int response_id, long user_data) { - responseID = response_id; + static void dialogResponseProc(long dialog, long response_id, long user_data) { + if (dialogAsyncFinish != null) { + dialogAsyncValue = dialogAsyncFinish.apply(response_id); + } + responseID = (int) response_id; } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/DirectoryDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/DirectoryDialog.java index e41896a07d8..7f58b6663e0 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/DirectoryDialog.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/DirectoryDialog.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -165,8 +165,12 @@ Optional openNativeChooserDialog () { byte [] titleBytes = Converter.wcsToMbcs (title, true); long shellHandle = parent.topHandle (); Display display = parent != null ? parent.getDisplay (): Display.getCurrent (); - long handle = 0; - handle = GTK.gtk_file_chooser_native_new(titleBytes, shellHandle, GTK.GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, null, null); + long handle; + if (GTK.GTK4) { + handle = GTK4.gtk_file_dialog_new(); + } else { + handle = GTK.gtk_file_chooser_native_new(titleBytes, shellHandle, GTK.GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, null, null); + } if (handle == 0) error (SWT.ERROR_NO_HANDLES); if (filterPath != null && filterPath.length () > 0) { @@ -186,7 +190,7 @@ Optional openNativeChooserDialog () { if (ptr != 0) { if (GTK.GTK4) { long file = OS.g_file_new_for_path(buffer); - GTK4.gtk_file_chooser_set_current_folder (handle, file, 0); + GTK4.gtk_file_dialog_set_initial_folder (handle, file); OS.g_object_unref(file); } else { GTK3.gtk_file_chooser_set_current_folder (handle, ptr); @@ -207,8 +211,12 @@ Optional openNativeChooserDialog () { } int response; + long file = 0; if (GTK.GTK4) { - response = SyncDialogUtil.run(display, handle, true); + file = SyncDialogUtil.run(display, + asyncCallback -> GTK4.gtk_file_dialog_select_folder(handle, shellHandle, 0, asyncCallback, 0), + asyncResult -> GTK4.gtk_file_dialog_select_folder_finish(handle, asyncResult, null)); + response = file != 0 ? GTK.GTK_RESPONSE_ACCEPT : GTK.GTK_RESPONSE_CANCEL; } else { display.externalEventLoop = true; display.sendPreExternalEventDispatchEvent (); @@ -223,7 +231,6 @@ Optional openNativeChooserDialog () { if (response == GTK.GTK_RESPONSE_ACCEPT) { long path; if (GTK.GTK4) { - long file = GTK4.gtk_file_chooser_get_file (handle); path = OS.g_file_get_path(file); } else { path = GTK3.gtk_file_chooser_get_filename (handle);