Skip to content

Commit 12bcd73

Browse files
ptzieglerakurtakov
authored andcommitted
[GTK4] Migrate DirectoryDialog from GtkFileChooser to GtkFileDialog
The FileChooser has been marked as deprecated with GTK 4.10 with the FileDialog being its replacement. Using this new API poses a challenge, as it requires the usage of the AsyncReadyCallback mechanism in order to respond to the closure of the dialog. Once the dialog has been opened via gtk_file_dialog_select_folder(), it is necessary to call gtk_file_dialog_select_folder_finish() from within the callback method. This method also returns the selected folder, which has to be cached until after the dialog has been closed. Following native methods have been added to GTK4: - gtk_file_dialog_new - gtk_file_dialog_select_folder - gtk_file_dialog_select_folder_finish - gtk_file_dialog_set_initial_folder
1 parent d908f99 commit 12bcd73

File tree

5 files changed

+136
-14
lines changed

5 files changed

+136
-14
lines changed

bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,54 @@ JNIEXPORT jboolean JNICALL GTK4_NATIVE(gtk_1file_1chooser_1set_1file)
704704
}
705705
#endif
706706

707+
#ifndef NO_gtk_1file_1dialog_1new
708+
JNIEXPORT jlong JNICALL GTK4_NATIVE(gtk_1file_1dialog_1new)
709+
(JNIEnv *env, jclass that)
710+
{
711+
jlong rc = 0;
712+
GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1new_FUNC);
713+
rc = (jlong)gtk_file_dialog_new();
714+
GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1new_FUNC);
715+
return rc;
716+
}
717+
#endif
718+
719+
#ifndef NO_gtk_1file_1dialog_1select_1folder
720+
JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1file_1dialog_1select_1folder)
721+
(JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2, jlong arg3, jlong arg4)
722+
{
723+
GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1select_1folder_FUNC);
724+
gtk_file_dialog_select_folder((GtkFileDialog *)arg0, (GtkWindow *)arg1, (GCancellable *)arg2, (GAsyncReadyCallback)arg3, (gpointer)arg4);
725+
GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1select_1folder_FUNC);
726+
}
727+
#endif
728+
729+
#ifndef NO_gtk_1file_1dialog_1select_1folder_1finish
730+
JNIEXPORT jlong JNICALL GTK4_NATIVE(gtk_1file_1dialog_1select_1folder_1finish)
731+
(JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlongArray arg2)
732+
{
733+
jlong *lparg2=NULL;
734+
jlong rc = 0;
735+
GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1select_1folder_1finish_FUNC);
736+
if (arg2) if ((lparg2 = (*env)->GetLongArrayElements(env, arg2, NULL)) == NULL) goto fail;
737+
rc = (jlong)gtk_file_dialog_select_folder_finish((GtkFileDialog *)arg0, (GAsyncResult *)arg1, (GError **)lparg2);
738+
fail:
739+
if (arg2 && lparg2) (*env)->ReleaseLongArrayElements(env, arg2, lparg2, 0);
740+
GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1select_1folder_1finish_FUNC);
741+
return rc;
742+
}
743+
#endif
744+
745+
#ifndef NO_gtk_1file_1dialog_1set_1initial_1folder
746+
JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1file_1dialog_1set_1initial_1folder)
747+
(JNIEnv *env, jclass that, jlong arg0, jlong arg1)
748+
{
749+
GTK4_NATIVE_ENTER(env, that, gtk_1file_1dialog_1set_1initial_1folder_FUNC);
750+
gtk_file_dialog_set_initial_folder((GtkFileDialog *)arg0, (GFile *)arg1);
751+
GTK4_NATIVE_EXIT(env, that, gtk_1file_1dialog_1set_1initial_1folder_FUNC);
752+
}
753+
#endif
754+
707755
#ifndef NO_gtk_1frame_1set_1child
708756
JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1frame_1set_1child)
709757
(JNIEnv *env, jclass that, jlong arg0, jlong arg1)

bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ typedef enum {
8181
gtk_1file_1chooser_1get_1files_FUNC,
8282
gtk_1file_1chooser_1set_1current_1folder_FUNC,
8383
gtk_1file_1chooser_1set_1file_FUNC,
84+
gtk_1file_1dialog_1new_FUNC,
85+
gtk_1file_1dialog_1select_1folder_FUNC,
86+
gtk_1file_1dialog_1select_1folder_1finish_FUNC,
87+
gtk_1file_1dialog_1set_1initial_1folder_FUNC,
8488
gtk_1frame_1set_1child_FUNC,
8589
gtk_1gesture_1click_1new_FUNC,
8690
gtk_1gesture_1drag_1new_FUNC,

bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2021, 2023 Syntevo and others.
2+
* Copyright (c) 2021, 2024 Syntevo and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -192,6 +192,28 @@ public class GTK4 {
192192
*/
193193
public static final native boolean gtk_file_chooser_set_file(long chooser, long file, long error);
194194

195+
/* GtkFileDialog */
196+
public static final native long gtk_file_dialog_new();
197+
/**
198+
* @param self cast=(GtkFileDialog *)
199+
* @param parent cast=(GtkWindow *)
200+
* @param cancellable cast=(GCancellable *)
201+
* @param callback cast=(GAsyncReadyCallback)
202+
* @param user_data cast=(gpointer)
203+
*/
204+
public static final native void gtk_file_dialog_select_folder(long self, long parent, long cancellable, long callback, long user_data);
205+
/**
206+
* @param self cast=(GtkFileDialog *)
207+
* @param result cast=(GAsyncResult *)
208+
* @param error cast=(GError **)
209+
*/
210+
public static final native long gtk_file_dialog_select_folder_finish(long self, long result, long[] error);
211+
/**
212+
* @param self cast=(GtkFileDialog *)
213+
* @param folder cast=(GFile *)
214+
*/
215+
public static final native void gtk_file_dialog_set_initial_folder(long self, long folder);
216+
195217
/* GtkScrolledWindow */
196218
public static final native long gtk_scrolled_window_new();
197219
/** @param scrolled_window cast=(GtkScrolledWindow *) */
@@ -609,11 +631,11 @@ public class GTK4 {
609631
public static final native long gdk_content_provider_new_union(long[] providers, int n_providers);
610632
/** @param formats cast=(GdkContentFormats *) */
611633
public static final native long gdk_content_formats_to_string(long formats);
612-
634+
613635
public static final native long gtk_gesture_rotate_new();
614636

615637
public static final native long gtk_gesture_zoom_new();
616-
638+
617639
public static final native long gtk_gesture_drag_new();
618640

619641
}

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/SyncDialogUtil.java

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2020 Red Hat Inc. and others.
2+
* Copyright (c) 2020, 2024 Red Hat Inc. and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -14,6 +14,7 @@
1414
package org.eclipse.swt.internal;
1515

1616
import java.lang.reflect.*;
17+
import java.util.function.*;
1718

1819
import org.eclipse.swt.internal.gtk.*;
1920
import org.eclipse.swt.widgets.*;
@@ -28,6 +29,41 @@
2829
public class SyncDialogUtil {
2930
static int responseID;
3031
static Callback dialogResponseCallback;
32+
static Function<Long, Long> dialogAsyncFinish;
33+
static Long dialogAsyncValue;
34+
35+
/**
36+
* This method implements the {@code AsyncReadyCallback} mechanism that is
37+
* used in GTK4. Most operations within GTK4 are executed asynchronously,
38+
* where the user is given the option to respond to the completion of such
39+
* an operation via a callback method, in order to e.g. process the result
40+
* or to apply additional cleanup tasks.<br>
41+
* When calling this method, the asynchronous operation is initiated via the
42+
* {code asyncOpen} parameter. Callers have to ensure that the callback
43+
* address is used as argument for the {@code AsyncReadyCallback} parameter.
44+
* From within the callback routine, the {@code asyncFinish} function is
45+
* called, receiving the {@code AsyncResult} of the callback as argument and
46+
* returning the {@code long} value of the callback function.<br>
47+
* This method blocks until the callback method has been called. It is
48+
* therefore essential that callers use the address of the {@link Callback}
49+
* as address for the {@code AsyncReadyCallback} object.
50+
*/
51+
static public long run(Display display, Consumer<Long> asyncOpen, Function<Long, Long> asyncFinish) {
52+
initializeResponseCallback();
53+
54+
dialogAsyncFinish = asyncFinish;
55+
asyncOpen.accept(dialogResponseCallback.getAddress());
56+
57+
while (!display.isDisposed()) {
58+
if (dialogAsyncValue != null) {
59+
break;
60+
}
61+
display.readAndDispatch();
62+
}
63+
64+
disposeResponseCallback();
65+
return dialogAsyncValue;
66+
}
3167

3268
/**
3369
* A blocking call that waits for the handling of the signal before returning
@@ -53,21 +89,23 @@ static public int run(Display display, long handle, boolean isNativeDialog) {
5389
}
5490

5591
disposeResponseCallback();
56-
return responseID;
92+
return (int) responseID;
5793
}
5894

5995
/**
6096
* Initializes the response callback and resets the responseID of the dialog to the default value.
6197
* This function should be called before connect the dialog to the "response" signal, as this sets up the callback.
6298
*/
6399
static void initializeResponseCallback() {
64-
dialogResponseCallback = new Callback(SyncDialogUtil.class, "dialogResponseProc", void.class, new Type[] {long.class, int.class, long.class});
100+
dialogResponseCallback = new Callback(SyncDialogUtil.class, "dialogResponseProc", void.class, new Type[] {long.class, long.class, long.class});
101+
dialogAsyncValue = null;
65102
responseID = -1;
66103
}
67104

68105
static void disposeResponseCallback() {
69106
dialogResponseCallback.dispose();
70107
dialogResponseCallback = null;
108+
dialogAsyncFinish = null;
71109
}
72110

73111
/**
@@ -77,7 +115,10 @@ static void disposeResponseCallback() {
77115
*
78116
* Note: Native dialogs are platform dialogs that don't use GtkDialog or GtkWindow.
79117
*/
80-
static void dialogResponseProc(long dialog, int response_id, long user_data) {
81-
responseID = response_id;
118+
static void dialogResponseProc(long dialog, long response_id, long user_data) {
119+
if (dialogAsyncFinish != null) {
120+
dialogAsyncValue = dialogAsyncFinish.apply(response_id);
121+
}
122+
responseID = (int) response_id;
82123
}
83124
}

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/DirectoryDialog.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2019 IBM Corporation and others.
2+
* Copyright (c) 2000, 2024 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -165,8 +165,12 @@ Optional<String> openNativeChooserDialog () {
165165
byte [] titleBytes = Converter.wcsToMbcs (title, true);
166166
long shellHandle = parent.topHandle ();
167167
Display display = parent != null ? parent.getDisplay (): Display.getCurrent ();
168-
long handle = 0;
169-
handle = GTK.gtk_file_chooser_native_new(titleBytes, shellHandle, GTK.GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, null, null);
168+
long handle;
169+
if (GTK.GTK4) {
170+
handle = GTK4.gtk_file_dialog_new();
171+
} else {
172+
handle = GTK.gtk_file_chooser_native_new(titleBytes, shellHandle, GTK.GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, null, null);
173+
}
170174
if (handle == 0) error (SWT.ERROR_NO_HANDLES);
171175

172176
if (filterPath != null && filterPath.length () > 0) {
@@ -186,7 +190,7 @@ Optional<String> openNativeChooserDialog () {
186190
if (ptr != 0) {
187191
if (GTK.GTK4) {
188192
long file = OS.g_file_new_for_path(buffer);
189-
GTK4.gtk_file_chooser_set_current_folder (handle, file, 0);
193+
GTK4.gtk_file_dialog_set_initial_folder (handle, file);
190194
OS.g_object_unref(file);
191195
} else {
192196
GTK3.gtk_file_chooser_set_current_folder (handle, ptr);
@@ -207,8 +211,12 @@ Optional<String> openNativeChooserDialog () {
207211
}
208212

209213
int response;
214+
long file = 0;
210215
if (GTK.GTK4) {
211-
response = SyncDialogUtil.run(display, handle, true);
216+
file = SyncDialogUtil.run(display,
217+
asyncCallback -> GTK4.gtk_file_dialog_select_folder(handle, shellHandle, 0, asyncCallback, 0),
218+
asyncResult -> GTK4.gtk_file_dialog_select_folder_finish(handle, asyncResult, null));
219+
response = file != 0 ? GTK.GTK_RESPONSE_ACCEPT : GTK.GTK_RESPONSE_CANCEL;
212220
} else {
213221
display.externalEventLoop = true;
214222
display.sendPreExternalEventDispatchEvent ();
@@ -223,7 +231,6 @@ Optional<String> openNativeChooserDialog () {
223231
if (response == GTK.GTK_RESPONSE_ACCEPT) {
224232
long path;
225233
if (GTK.GTK4) {
226-
long file = GTK4.gtk_file_chooser_get_file (handle);
227234
path = OS.g_file_get_path(file);
228235
} else {
229236
path = GTK3.gtk_file_chooser_get_filename (handle);

0 commit comments

Comments
 (0)