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