1 /*
2 * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #include <jni.h>
27 #include <stdio.h>
28 #include <jni_util.h>
29 #include <string.h>
30 #include "gtk_interface.h"
31 #include "sun_awt_X11_GtkFileDialogPeer.h"
32 #include "java_awt_FileDialog.h"
33 #include "debug_assert.h"
34
35 typedef void GtkWidget;
36 static JavaVM *jvm;
37
38 /* To cache some method IDs */
39 static jmethodID filenameFilterCallbackMethodID = NULL;
40 static jmethodID setFileInternalMethodID = NULL;
41 static jfieldID widgetFieldID = NULL;
42
Java_sun_awt_X11_GtkFileDialogPeer_initIDs(JNIEnv * env,jclass cx)43 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_initIDs
44 (JNIEnv *env, jclass cx)
45 {
46 filenameFilterCallbackMethodID = (*env)->GetMethodID(env, cx,
47 "filenameFilterCallback", "(Ljava/lang/String;)Z");
48 DASSERT(filenameFilterCallbackMethodID != NULL);
49 CHECK_NULL(filenameFilterCallbackMethodID);
50
51 setFileInternalMethodID = (*env)->GetMethodID(env, cx,
52 "setFileInternal", "(Ljava/lang/String;[Ljava/lang/String;)V");
53 DASSERT(setFileInternalMethodID != NULL);
54 CHECK_NULL(setFileInternalMethodID);
55
56 widgetFieldID = (*env)->GetFieldID(env, cx, "widget", "J");
57 DASSERT(widgetFieldID != NULL);
58 }
59
filenameFilterCallback(const GtkFileFilterInfo * filter_info,gpointer obj)60 static gboolean filenameFilterCallback(const GtkFileFilterInfo * filter_info, gpointer obj)
61 {
62 JNIEnv *env;
63 jstring filename;
64
65 env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
66
67 filename = (*env)->NewStringUTF(env, filter_info->filename);
68 JNU_CHECK_EXCEPTION_RETURN(env, FALSE);
69
70 return (*env)->CallBooleanMethod(env, obj, filenameFilterCallbackMethodID,
71 filename);
72 }
73
quit(JNIEnv * env,jobject jpeer,gboolean isSignalHandler)74 static void quit(JNIEnv * env, jobject jpeer, gboolean isSignalHandler)
75 {
76 jthrowable pendingException;
77 if (pendingException = (*env)->ExceptionOccurred(env)) {
78 (*env)->ExceptionClear(env);
79 }
80
81 GtkWidget * dialog = (GtkWidget*)jlong_to_ptr(
82 (*env)->GetLongField(env, jpeer, widgetFieldID));
83
84 if (dialog != NULL)
85 {
86 // Callbacks from GTK signals are made within the GTK lock
87 // So, within a signal handler there is no need to call
88 // gdk_threads_enter() / gtk->gdk_threads_leave()
89 if (!isSignalHandler) {
90 gtk->gdk_threads_enter();
91 }
92
93 gtk->gtk_widget_hide (dialog);
94 gtk->gtk_widget_destroy (dialog);
95
96 gtk->gtk_main_quit ();
97
98 (*env)->SetLongField(env, jpeer, widgetFieldID, 0);
99
100 if (!isSignalHandler) {
101 gtk->gdk_threads_leave();
102 }
103 }
104
105 if (pendingException) {
106 (*env)->Throw(env, pendingException);
107 }
108 }
109
110 /*
111 * Class: sun_awt_X11_GtkFileDialogPeer
112 * Method: quit
113 * Signature: ()V
114 */
Java_sun_awt_X11_GtkFileDialogPeer_quit(JNIEnv * env,jobject jpeer)115 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_quit
116 (JNIEnv * env, jobject jpeer)
117 {
118 quit(env, jpeer, FALSE);
119 }
120
121 /*
122 * Class: sun_awt_X11_GtkFileDialogPeer
123 * Method: toFront
124 * Signature: ()V
125 */
Java_sun_awt_X11_GtkFileDialogPeer_toFront(JNIEnv * env,jobject jpeer)126 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_toFront
127 (JNIEnv * env, jobject jpeer)
128 {
129 GtkWidget * dialog;
130
131 gtk->gdk_threads_enter();
132
133 dialog = (GtkWidget*)jlong_to_ptr(
134 (*env)->GetLongField(env, jpeer, widgetFieldID));
135
136 if (dialog != NULL) {
137 gtk->gtk_window_present((GtkWindow*)dialog);
138 }
139
140 gtk->gdk_threads_leave();
141 }
142
143 /*
144 * Class: sun_awt_X11_GtkFileDialogPeer
145 * Method: setBounds
146 * Signature: (IIIII)V
147 */
Java_sun_awt_X11_GtkFileDialogPeer_setBounds(JNIEnv * env,jobject jpeer,jint x,jint y,jint width,jint height,jint op)148 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_setBounds
149 (JNIEnv * env, jobject jpeer, jint x, jint y, jint width, jint height, jint op)
150 {
151 GtkWindow* dialog;
152
153 gtk->gdk_threads_enter();
154
155 dialog = (GtkWindow*)jlong_to_ptr(
156 (*env)->GetLongField(env, jpeer, widgetFieldID));
157
158 if (dialog != NULL) {
159 if (x >= 0 && y >= 0) {
160 gtk->gtk_window_move(dialog, (gint)x, (gint)y);
161 }
162 if (width > 0 && height > 0) {
163 gtk->gtk_window_resize(dialog, (gint)width, (gint)height);
164 }
165 }
166
167 gtk->gdk_threads_leave();
168 }
169
170 /*
171 * baseDir should be freed by user.
172 */
isFromSameDirectory(GSList * list,gchar ** baseDir)173 static gboolean isFromSameDirectory(GSList* list, gchar** baseDir) {
174
175 GSList *it = list;
176 gchar* prevDir = NULL;
177 gboolean isAllDirsSame = TRUE;
178
179 while (it) {
180 gchar* dir = gtk->g_path_get_dirname((gchar*) it->data);
181
182 if (prevDir && strcmp(prevDir, dir) != 0) {
183 isAllDirsSame = FALSE;
184 gtk->g_free(dir);
185 break;
186 }
187
188 if (!prevDir) {
189 prevDir = strdup(dir);
190 }
191 gtk->g_free(dir);
192
193 it = it->next;
194 }
195
196 if (isAllDirsSame) {
197 *baseDir = prevDir;
198 } else {
199 free(prevDir);
200 *baseDir = strdup("/");
201 }
202
203 return isAllDirsSame;
204 }
205
206 /**
207 * Convert a GSList to an array of filenames
208 */
toFilenamesArray(JNIEnv * env,GSList * list,jstring * jcurrent_folder)209 static jobjectArray toFilenamesArray(JNIEnv *env, GSList* list, jstring* jcurrent_folder)
210 {
211 jstring str;
212 jclass stringCls;
213 GSList *iterator;
214 jobjectArray array;
215 int i;
216 gchar* entry;
217 gchar * baseDir;
218 gboolean isFromSameDir;
219
220 if (list == NULL) {
221 return NULL;
222 }
223
224 stringCls = (*env)->FindClass(env, "java/lang/String");
225 if (stringCls == NULL) {
226 (*env)->ExceptionClear(env);
227 JNU_ThrowInternalError(env, "Could not get java.lang.String class");
228 return NULL;
229 }
230
231 array = (*env)->NewObjectArray(env, gtk->gtk_g_slist_length(list), stringCls, NULL);
232 if (array == NULL) {
233 (*env)->ExceptionClear(env);
234 JNU_ThrowInternalError(env, "Could not instantiate array files array");
235 return NULL;
236 }
237
238 isFromSameDir = isFromSameDirectory(list, &baseDir);
239
240 *jcurrent_folder = (*env)->NewStringUTF(env, baseDir);
241 if (*jcurrent_folder == NULL) {
242 free(baseDir);
243 return NULL;
244 }
245
246 for (iterator = list, i=0;
247 iterator;
248 iterator = iterator->next, i++) {
249
250 entry = (gchar*) iterator->data;
251
252 if (isFromSameDir) {
253 entry = strrchr(entry, '/') + 1;
254 } else if (entry[0] == '/') {
255 entry++;
256 }
257
258 str = (*env)->NewStringUTF(env, entry);
259 if((*env)->ExceptionCheck(env)){
260 break;
261 }
262 if (str) {
263 (*env)->SetObjectArrayElement(env, array, i, str);
264 if((*env)->ExceptionCheck(env)){
265 break;
266 }
267 }
268 }
269
270 free(baseDir);
271 return array;
272 }
273
handle_response(GtkWidget * aDialog,gint responseId,gpointer obj)274 static void handle_response(GtkWidget* aDialog, gint responseId, gpointer obj)
275 {
276 JNIEnv *env;
277 GSList *filenames;
278 jstring jcurrent_folder = NULL;
279 jobjectArray jfilenames;
280
281 env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
282 filenames = NULL;
283
284 if (responseId == GTK_RESPONSE_ACCEPT) {
285 filenames = gtk->gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(aDialog));
286 }
287
288 jfilenames = toFilenamesArray(env, filenames, &jcurrent_folder);
289
290 if (!(*env)->ExceptionCheck(env)) {
291 (*env)->CallVoidMethod(env, obj, setFileInternalMethodID,
292 jcurrent_folder, jfilenames);
293 }
294
295 quit(env, (jobject)obj, TRUE);
296 }
297
298 /*
299 * Class: sun_awt_X11_GtkFileDialogPeer
300 * Method: run
301 * Signature: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/io/FilenameFilter;ZII)V
302 */
303 JNIEXPORT void JNICALL
Java_sun_awt_X11_GtkFileDialogPeer_run(JNIEnv * env,jobject jpeer,jstring jtitle,jint mode,jstring jdir,jstring jfile,jobject jfilter,jboolean multiple,int x,int y)304 Java_sun_awt_X11_GtkFileDialogPeer_run(JNIEnv * env, jobject jpeer,
305 jstring jtitle, jint mode, jstring jdir, jstring jfile,
306 jobject jfilter, jboolean multiple, int x, int y)
307 {
308 GtkWidget *dialog = NULL;
309 GtkFileFilter *filter;
310
311 if (jvm == NULL) {
312 (*env)->GetJavaVM(env, &jvm);
313 JNU_CHECK_EXCEPTION(env);
314 }
315
316 gtk->gdk_threads_enter();
317
318 const char *title = jtitle == NULL? "": (*env)->GetStringUTFChars(env, jtitle, 0);
319 if (title == NULL) {
320 (*env)->ExceptionClear(env);
321 JNU_ThrowOutOfMemoryError(env, "Could not get title");
322 return;
323 }
324
325 if (mode == java_awt_FileDialog_SAVE) {
326 /* Save action */
327 dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
328 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
329 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
330 }
331 else {
332 /* Default action OPEN */
333 dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
334 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
335 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
336
337 /* Set multiple selection mode, that is allowed only in OPEN action */
338 if (multiple) {
339 gtk->gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),
340 multiple);
341 }
342 }
343
344 if (jtitle != NULL) {
345 (*env)->ReleaseStringUTFChars(env, jtitle, title);
346 }
347
348 /* Set the directory */
349 if (jdir != NULL) {
350 const char *dir = (*env)->GetStringUTFChars(env, jdir, 0);
351 if (dir == NULL) {
352 (*env)->ExceptionClear(env);
353 JNU_ThrowOutOfMemoryError(env, "Could not get dir");
354 return;
355 }
356 gtk->gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir);
357 (*env)->ReleaseStringUTFChars(env, jdir, dir);
358 }
359
360 /* Set the filename */
361 if (jfile != NULL) {
362 const char *filename = (*env)->GetStringUTFChars(env, jfile, 0);
363 if (filename == NULL) {
364 (*env)->ExceptionClear(env);
365 JNU_ThrowOutOfMemoryError(env, "Could not get filename");
366 return;
367 }
368 if (mode == java_awt_FileDialog_SAVE) {
369 gtk->gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
370 } else {
371 gtk->gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), filename);
372 }
373 (*env)->ReleaseStringUTFChars(env, jfile, filename);
374 }
375
376 /* Set the file filter */
377 if (jfilter != NULL) {
378 filter = gtk->gtk_file_filter_new();
379 gtk->gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
380 filenameFilterCallback, jpeer, NULL);
381 gtk->gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
382 }
383
384 /* Other Properties */
385 if (gtk->gtk_check_version(2, 8, 0) == NULL ||
386 gtk->gtk_check_version(3, 0, 0) == NULL) {
387 gtk->gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(
388 dialog), TRUE);
389 }
390
391 /* Set the initial location */
392 if (x >= 0 && y >= 0) {
393 gtk->gtk_window_move((GtkWindow*)dialog, (gint)x, (gint)y);
394
395 // NOTE: it doesn't set the initial size for the file chooser
396 // as it seems like the file chooser overrides the size internally
397 }
398
399 gtk->g_signal_connect_data(dialog, "response", G_CALLBACK(
400 handle_response), jpeer, 0, 0);
401
402 (*env)->SetLongField(env, jpeer, widgetFieldID, ptr_to_jlong(dialog));
403
404 gtk->gtk_widget_show(dialog);
405
406 gtk->gtk_main();
407 gtk->gdk_threads_leave();
408 }
409
410