1 #include <jni.h> 2 #include <stdio.h> 3 #include <jni_util.h> 4 #include <string.h> 5 #include "gtk2_interface.h" 6 #include "sun_awt_X11_GtkFileDialogPeer.h" 7 #include "java_awt_FileDialog.h" 8 #include "debug_assert.h" 9 10 static JavaVM *jvm; 11 12 /* To cache some method IDs */ 13 static jmethodID filenameFilterCallbackMethodID = NULL; 14 static jmethodID setFileInternalMethodID = NULL; 15 static jfieldID widgetFieldID = NULL; 16 17 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_initIDs 18 (JNIEnv *env, jclass cx) 19 { 20 filenameFilterCallbackMethodID = (*env)->GetMethodID(env, cx, 21 "filenameFilterCallback", "(Ljava/lang/String;)Z"); 22 DASSERT(filenameFilterCallbackMethodID != NULL); 23 24 setFileInternalMethodID = (*env)->GetMethodID(env, cx, 25 "setFileInternal", "(Ljava/lang/String;[Ljava/lang/String;)V"); 26 DASSERT(setFileInternalMethodID != NULL); 27 28 widgetFieldID = (*env)->GetFieldID(env, cx, "widget", "J"); 29 DASSERT(widgetFieldID != NULL); 30 } 31 32 static gboolean filenameFilterCallback(const GtkFileFilterInfo * filter_info, gpointer obj) 33 { 34 JNIEnv *env; 35 jclass cx; 36 jstring filename; 37 38 env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2); 39 40 filename = (*env)->NewStringUTF(env, filter_info->filename); 41 42 return (*env)->CallBooleanMethod(env, obj, filenameFilterCallbackMethodID, 43 filename); 44 } 45 46 static void quit(JNIEnv * env, jobject jpeer, gboolean isSignalHandler) 47 { 48 GtkWidget * dialog = (GtkWidget*)jlong_to_ptr( 49 (*env)->GetLongField(env, jpeer, widgetFieldID)); 50 51 if (dialog != NULL) 52 { 53 // Callbacks from GTK signals are made within the GTK lock 54 // So, within a signal handler there is no need to call 55 // gdk_threads_enter() / fp_gdk_threads_leave() 56 if (!isSignalHandler) { 57 fp_gdk_threads_enter(); 58 } 59 60 fp_gtk_widget_hide (dialog); 61 fp_gtk_widget_destroy (dialog); 62 63 fp_gtk_main_quit (); 64 65 (*env)->SetLongField(env, jpeer, widgetFieldID, 0); 66 67 if (!isSignalHandler) { 68 fp_gdk_threads_leave(); 69 } 70 } 71 } 72 73 /* 74 * Class: sun_awt_X11_GtkFileDialogPeer 75 * Method: quit 76 * Signature: ()V 77 */ 78 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_quit 79 (JNIEnv * env, jobject jpeer) 80 { 81 quit(env, jpeer, FALSE); 82 } 83 84 /* 85 * Class: sun_awt_X11_GtkFileDialogPeer 86 * Method: toFront 87 * Signature: ()V 88 */ 89 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_toFront 90 (JNIEnv * env, jobject jpeer) 91 { 92 GtkWidget * dialog; 93 94 fp_gdk_threads_enter(); 95 96 dialog = (GtkWidget*)jlong_to_ptr( 97 (*env)->GetLongField(env, jpeer, widgetFieldID)); 98 99 if (dialog != NULL) { 100 fp_gtk_window_present((GtkWindow*)dialog); 101 } 102 103 fp_gdk_threads_leave(); 104 } 105 106 /* 107 * Class: sun_awt_X11_GtkFileDialogPeer 108 * Method: setBounds 109 * Signature: (IIIII)V 110 */ 111 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_setBounds 112 (JNIEnv * env, jobject jpeer, jint x, jint y, jint width, jint height, jint op) 113 { 114 GtkWindow* dialog; 115 116 fp_gdk_threads_enter(); 117 118 dialog = (GtkWindow*)jlong_to_ptr( 119 (*env)->GetLongField(env, jpeer, widgetFieldID)); 120 121 if (dialog != NULL) { 122 if (x >= 0 && y >= 0) { 123 fp_gtk_window_move(dialog, (gint)x, (gint)y); 124 } 125 if (width > 0 && height > 0) { 126 fp_gtk_window_resize(dialog, (gint)width, (gint)height); 127 } 128 } 129 130 fp_gdk_threads_leave(); 131 } 132 133 /** 134 * Convert a GSList to an array of filenames (without the parent folder) 135 */ 136 static jobjectArray toFilenamesArray(JNIEnv *env, GSList* list) 137 { 138 jstring str; 139 jclass stringCls; 140 GSList *iterator; 141 jobjectArray array; 142 int i; 143 char* entry; 144 145 if (NULL == list) { 146 return NULL; 147 } 148 149 stringCls = (*env)->FindClass(env, "java/lang/String"); 150 if (stringCls == NULL) { 151 JNU_ThrowInternalError(env, "Could not get java.lang.String class"); 152 return NULL; 153 } 154 155 array = (*env)->NewObjectArray(env, fp_gtk_g_slist_length(list), stringCls, 156 NULL); 157 if (array == NULL) { 158 JNU_ThrowInternalError(env, "Could not instantiate array files array"); 159 return NULL; 160 } 161 162 i = 0; 163 for (iterator = list; iterator; iterator = iterator->next) { 164 entry = (char*) iterator->data; 165 entry = strrchr(entry, '/') + 1; 166 str = (*env)->NewStringUTF(env, entry); 167 (*env)->SetObjectArrayElement(env, array, i, str); 168 i++; 169 } 170 171 return array; 172 } 173 174 static void handle_response(GtkWidget* aDialog, gint responseId, gpointer obj) 175 { 176 JNIEnv *env; 177 char *current_folder; 178 GSList *filenames; 179 jclass cx; 180 jstring jcurrent_folder; 181 jobjectArray jfilenames; 182 183 env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2); 184 current_folder = NULL; 185 filenames = NULL; 186 187 if (responseId == GTK_RESPONSE_ACCEPT) { 188 current_folder = fp_gtk_file_chooser_get_current_folder( 189 GTK_FILE_CHOOSER(aDialog)); 190 filenames = fp_gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(aDialog)); 191 } 192 193 jcurrent_folder = (*env)->NewStringUTF(env, current_folder); 194 jfilenames = toFilenamesArray(env, filenames); 195 196 (*env)->CallVoidMethod(env, obj, setFileInternalMethodID, jcurrent_folder, 197 jfilenames); 198 fp_g_free(current_folder); 199 200 quit(env, (jobject)obj, TRUE); 201 } 202 203 /* 204 * Class: sun_awt_X11_GtkFileDialogPeer 205 * Method: run 206 * Signature: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/io/FilenameFilter;ZII)V 207 */ 208 JNIEXPORT void JNICALL 209 Java_sun_awt_X11_GtkFileDialogPeer_run(JNIEnv * env, jobject jpeer, 210 jstring jtitle, jint mode, jstring jdir, jstring jfile, 211 jobject jfilter, jboolean multiple, int x, int y) 212 { 213 GtkWidget *dialog = NULL; 214 GtkFileFilter *filter; 215 216 if (jvm == NULL) { 217 (*env)->GetJavaVM(env, &jvm); 218 } 219 220 fp_gdk_threads_enter(); 221 222 const char *title = jtitle == NULL? "": (*env)->GetStringUTFChars(env, jtitle, 0); 223 224 if (mode == java_awt_FileDialog_SAVE) { 225 /* Save action */ 226 dialog = fp_gtk_file_chooser_dialog_new(title, NULL, 227 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, 228 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); 229 } 230 else { 231 /* Default action OPEN */ 232 dialog = fp_gtk_file_chooser_dialog_new(title, NULL, 233 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, 234 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); 235 236 /* Set multiple selection mode, that is allowed only in OPEN action */ 237 if (multiple) { 238 fp_gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), 239 multiple); 240 } 241 } 242 243 if (jtitle != NULL) { 244 (*env)->ReleaseStringUTFChars(env, jtitle, title); 245 } 246 247 /* Set the directory */ 248 if (jdir != NULL) { 249 const char *dir = (*env)->GetStringUTFChars(env, jdir, 0); 250 fp_gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir); 251 (*env)->ReleaseStringUTFChars(env, jdir, dir); 252 } 253 254 /* Set the filename */ 255 if (jfile != NULL) { 256 const char *filename = (*env)->GetStringUTFChars(env, jfile, 0); 257 if (mode == java_awt_FileDialog_SAVE) { 258 fp_gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename); 259 } else { 260 fp_gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), filename); 261 } 262 (*env)->ReleaseStringUTFChars(env, jfile, filename); 263 } 264 265 /* Set the file filter */ 266 if (jfilter != NULL) { 267 filter = fp_gtk_file_filter_new(); 268 fp_gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, 269 filenameFilterCallback, jpeer, NULL); 270 fp_gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); 271 } 272 273 /* Other Properties */ 274 if (fp_gtk_check_version(2, 8, 0) == NULL) { 275 fp_gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER( 276 dialog), TRUE); 277 } 278 279 /* Set the initial location */ 280 if (x >= 0 && y >= 0) { 281 fp_gtk_window_move((GtkWindow*)dialog, (gint)x, (gint)y); 282 283 // NOTE: it doesn't set the initial size for the file chooser 284 // as it seems like the file chooser overrides the size internally 285 } 286 287 fp_g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK( 288 handle_response), jpeer); 289 290 (*env)->SetLongField(env, jpeer, widgetFieldID, ptr_to_jlong(dialog)); 291 292 fp_gtk_widget_show(dialog); 293 294 fp_gtk_main(); 295 fp_gdk_threads_leave(); 296 } 297