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 /**
 175  * Convert a GSList to an array of filenames (with the parent folder)
 176  */
 177 static jobjectArray toPathAndFilenamesArray(JNIEnv *env, GSList* list)
 178 {
 179     jstring str;
 180     jclass stringCls;
 181     GSList *iterator;
 182     jobjectArray array;
 183     int i;
 184     char* entry;
 185 
 186 
 187     if (NULL == list) {
 188         return NULL;
 189     }
 190 
 191     stringCls = (*env)->FindClass(env, "java/lang/String");
 192     if (stringCls == NULL) {
 193         JNU_ThrowInternalError(env, "Could not get java.lang.String class");
 194         return NULL;
 195     }
 196 
 197     array = (*env)->NewObjectArray(env, fp_gtk_g_slist_length(list), stringCls,
 198             NULL);
 199     if (array == NULL) {
 200         JNU_ThrowInternalError(env, "Could not instantiate array files array");
 201         return NULL;
 202     }
 203 
 204     i = 0;
 205     for (iterator = list; iterator; iterator = iterator->next) {
 206         entry = (char*) iterator->data;
 207         //check for leading slash.
 208         if( (entry-strchr(entry, '/')) == 0) entry++;
 209         
 210         str = (*env)->NewStringUTF(env, entry);
 211         (*env)->SetObjectArrayElement(env, array, i, str);
 212         i++;
 213     }
 214 
 215     return array;
 216 }
 217 
 218 static void handle_response(GtkWidget* aDialog, gint responseId, gpointer obj)
 219 {
 220     JNIEnv *env;
 221     char *current_folder;
 222     GSList *filenames;
 223     jclass cx;
 224     jstring jcurrent_folder;
 225     jobjectArray jfilenames;
 226 
 227     env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
 228     current_folder = NULL;
 229     filenames = NULL;
 230     gboolean full_path_names = FALSE;
 231     
 232     if (responseId == GTK_RESPONSE_ACCEPT) {
 233         current_folder = fp_gtk_file_chooser_get_current_folder(
 234                 GTK_FILE_CHOOSER(aDialog));
 235         if(NULL == current_folder){
 236             full_path_names=TRUE;
 237         }
 238         filenames = fp_gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(aDialog));
 239     }
 240     if(full_path_names){
 241         //This is a hack for use with "Recent Folders" in gtk where each
 242         //file could have its own directory.
 243         jcurrent_folder = (*env)->NewStringUTF(env, "/");
 244         jfilenames = toPathAndFilenamesArray(env, filenames);
 245     
 246     } else{
 247         jcurrent_folder = (*env)->NewStringUTF(env, current_folder);
 248         jfilenames = toFilenamesArray(env, filenames);
 249     }
 250     (*env)->CallVoidMethod(env, obj, setFileInternalMethodID, jcurrent_folder,
 251             jfilenames);
 252     fp_g_free(current_folder);
 253 
 254     quit(env, (jobject)obj, TRUE);
 255 }
 256 
 257 /*
 258  * Class:     sun_awt_X11_GtkFileDialogPeer
 259  * Method:    run
 260  * Signature: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/io/FilenameFilter;ZII)V
 261  */
 262 JNIEXPORT void JNICALL
 263 Java_sun_awt_X11_GtkFileDialogPeer_run(JNIEnv * env, jobject jpeer,
 264         jstring jtitle, jint mode, jstring jdir, jstring jfile,
 265         jobject jfilter, jboolean multiple, int x, int y)
 266 {
 267     GtkWidget *dialog = NULL;
 268     GtkFileFilter *filter;
 269 
 270     if (jvm == NULL) {
 271         (*env)->GetJavaVM(env, &jvm);
 272     }
 273 
 274     fp_gdk_threads_enter();
 275 
 276     const char *title = jtitle == NULL? "": (*env)->GetStringUTFChars(env, jtitle, 0);
 277 
 278     if (mode == java_awt_FileDialog_SAVE) {
 279         /* Save action */
 280         dialog = fp_gtk_file_chooser_dialog_new(title, NULL,
 281                 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
 282                 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
 283     }
 284     else {
 285         /* Default action OPEN */
 286         dialog = fp_gtk_file_chooser_dialog_new(title, NULL,
 287                 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
 288                 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
 289 
 290         /* Set multiple selection mode, that is allowed only in OPEN action */
 291         if (multiple) {
 292             fp_gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),
 293                     multiple);
 294         }
 295     }
 296 
 297     if (jtitle != NULL) {
 298       (*env)->ReleaseStringUTFChars(env, jtitle, title);
 299     }
 300 
 301     /* Set the directory */
 302     if (jdir != NULL) {
 303         const char *dir = (*env)->GetStringUTFChars(env, jdir, 0);
 304         fp_gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir);
 305         (*env)->ReleaseStringUTFChars(env, jdir, dir);
 306     }
 307 
 308     /* Set the filename */
 309     if (jfile != NULL) {
 310         const char *filename = (*env)->GetStringUTFChars(env, jfile, 0);
 311         if (mode == java_awt_FileDialog_SAVE) {
 312             fp_gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
 313         } else {
 314             fp_gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), filename);
 315         }
 316         (*env)->ReleaseStringUTFChars(env, jfile, filename);
 317     }
 318 
 319     /* Set the file filter */
 320     if (jfilter != NULL) {
 321         filter = fp_gtk_file_filter_new();
 322         fp_gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
 323                 filenameFilterCallback, jpeer, NULL);
 324         fp_gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
 325     }
 326 
 327     /* Other Properties */
 328     if (fp_gtk_check_version(2, 8, 0) == NULL) {
 329         fp_gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(
 330                 dialog), TRUE);
 331     }
 332 
 333     /* Set the initial location */
 334     if (x >= 0 && y >= 0) {
 335         fp_gtk_window_move((GtkWindow*)dialog, (gint)x, (gint)y);
 336 
 337         // NOTE: it doesn't set the initial size for the file chooser
 338         // as it seems like the file chooser overrides the size internally
 339     }
 340 
 341     fp_g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(
 342             handle_response), jpeer);
 343 
 344     (*env)->SetLongField(env, jpeer, widgetFieldID, ptr_to_jlong(dialog));
 345 
 346     fp_gtk_widget_show(dialog);
 347 
 348     fp_gtk_main();
 349     fp_gdk_threads_leave();
 350 }
 351