1 /*
   2  * Copyright (c) 2011, 2016, 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 #include <com_sun_glass_ui_gtk_GtkCommonDialogs.h>
  26 #include "glass_general.h"
  27 #include "glass_window.h"
  28 
  29 #include <gdk/gdk.h>
  30 #include <gtk/gtk.h>
  31 
  32 #include <cstring>
  33 #include <cstdlib>
  34 
  35 static GSList* setup_GtkFileFilters(GtkFileChooser*, JNIEnv*, jobjectArray, int default_filter_index);
  36 
  37 static void free_fname(char* fname, gpointer unused) {
  38     (void)unused;
  39 
  40     g_free(fname);
  41 }
  42 
  43 static gboolean jstring_to_utf_get(JNIEnv *env, jstring jstr,
  44                                    const char **cstr) {
  45     const char *newstr;
  46 
  47     if (jstr == NULL) {
  48         *cstr = NULL;
  49         return TRUE;
  50     }
  51 
  52     newstr = env->GetStringUTFChars(jstr, NULL);
  53     if (newstr != NULL) {
  54         *cstr = newstr;
  55         return TRUE;
  56     }
  57 
  58     return FALSE;
  59 }
  60 
  61 static void jstring_to_utf_release(JNIEnv *env, jstring jstr,
  62                                    const char *cstr) {
  63     if (cstr != NULL) {
  64         env->ReleaseStringUTFChars(jstr, cstr);
  65     }
  66 }
  67 
  68 static GtkWindow *gdk_window_handle_to_gtk(jlong handle) {
  69     return  (handle != 0)
  70                 ? ((WindowContext*)JLONG_TO_PTR(handle))->get_gtk_window()
  71                 : NULL;
  72 }
  73 
  74 static jobject create_empty_result() {
  75     jclass jFileChooserResult = (jclass) mainEnv->FindClass("com/sun/glass/ui/CommonDialogs$FileChooserResult");
  76     if (EXCEPTION_OCCURED(mainEnv)) return NULL;
  77     jmethodID jFileChooserResultInit = mainEnv->GetMethodID(jFileChooserResult, "<init>", "()V");
  78     if (EXCEPTION_OCCURED(mainEnv)) return NULL;
  79     jobject jResult = mainEnv->NewObject(jFileChooserResult, jFileChooserResultInit);
  80     if (EXCEPTION_OCCURED(mainEnv)) return NULL;
  81     return jResult;
  82 }
  83 
  84 extern "C" {
  85 
  86 JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_gtk_GtkCommonDialogs__1showFileChooser
  87   (JNIEnv *env, jclass clazz, jlong parent, jstring folder, jstring name, jstring title,
  88    jint type, jboolean multiple, jobjectArray jFilters, jint default_filter_index) {
  89     (void)clazz;
  90 
  91     jobjectArray jFileNames = NULL;
  92     char* filename;
  93     jstring jfilename;
  94 
  95     const char* chooser_folder;
  96     const char* chooser_filename;
  97     const char* chooser_title;
  98     const int chooser_type = type == 0 ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE;
  99 
 100     if (!jstring_to_utf_get(env, folder, &chooser_folder)) {
 101         return create_empty_result();
 102     }
 103 
 104     if (!jstring_to_utf_get(env, title, &chooser_title)) {
 105         jstring_to_utf_release(env, folder, chooser_folder);
 106         return create_empty_result();
 107     }
 108 
 109     if (!jstring_to_utf_get(env, name, &chooser_filename)) {
 110         jstring_to_utf_release(env, folder, chooser_folder);
 111         jstring_to_utf_release(env, title, chooser_title);
 112         return create_empty_result();
 113     }
 114 
 115     GtkWidget* chooser = gtk_file_chooser_dialog_new(chooser_title, gdk_window_handle_to_gtk(parent),
 116             static_cast<GtkFileChooserAction>(chooser_type),
 117             GTK_STOCK_CANCEL,
 118             GTK_RESPONSE_CANCEL,
 119             (chooser_type == GTK_FILE_CHOOSER_ACTION_OPEN ? GTK_STOCK_OPEN : GTK_STOCK_SAVE),
 120             GTK_RESPONSE_ACCEPT,
 121             NULL);
 122 
 123     if (chooser_type == GTK_FILE_CHOOSER_ACTION_SAVE) {
 124         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser), chooser_filename);
 125         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (chooser), TRUE);
 126     }
 127 
 128     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), (JNI_TRUE == multiple));
 129     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), chooser_folder);
 130     GSList* filters = setup_GtkFileFilters(GTK_FILE_CHOOSER(chooser), env, jFilters, default_filter_index);
 131 
 132     if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
 133         GSList* fnames_gslist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chooser));
 134         guint fnames_list_len = g_slist_length(fnames_gslist);
 135         LOG1("FileChooser selected files: %d\n", fnames_list_len)
 136 
 137         if (fnames_list_len > 0) {
 138             jFileNames = env->NewObjectArray((jsize)fnames_list_len, jStringCls, NULL);
 139             EXCEPTION_OCCURED(env);
 140             for (guint i = 0; i < fnames_list_len; i++) {
 141                 filename = (char*)g_slist_nth(fnames_gslist, i)->data;
 142                 LOG1("Add [%s] into returned filenames\n", filename)
 143                 jfilename = env->NewStringUTF(filename);
 144                 EXCEPTION_OCCURED(env);
 145                 env->SetObjectArrayElement(jFileNames, (jsize)i, jfilename);
 146                 EXCEPTION_OCCURED(env);
 147             }
 148             g_slist_foreach(fnames_gslist, (GFunc) free_fname, NULL);
 149             g_slist_free(fnames_gslist);
 150         }
 151     }
 152 
 153     if (!jFileNames) {
 154         jFileNames = env->NewObjectArray(0, jStringCls, NULL);
 155         EXCEPTION_OCCURED(env);
 156     }
 157 
 158     int index = g_slist_index(filters, gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(chooser)));
 159 
 160     jclass jCommonDialogs = (jclass) env->FindClass("com/sun/glass/ui/CommonDialogs");
 161     EXCEPTION_OCCURED(env);
 162     jmethodID jCreateFileChooserResult = env->GetStaticMethodID(jCommonDialogs,
 163             "createFileChooserResult",
 164             "([Ljava/lang/String;[Lcom/sun/glass/ui/CommonDialogs$ExtensionFilter;I)Lcom/sun/glass/ui/CommonDialogs$FileChooserResult;");
 165 
 166     EXCEPTION_OCCURED(env);
 167 
 168     jobject result =
 169             env->CallStaticObjectMethod(jCommonDialogs, jCreateFileChooserResult, jFileNames, jFilters, index);
 170     LOG_EXCEPTION(env)
 171 
 172     g_slist_free(filters);
 173     gtk_widget_destroy(chooser);
 174 
 175     jstring_to_utf_release(env, folder, chooser_folder);
 176     jstring_to_utf_release(env, title, chooser_title);
 177     jstring_to_utf_release(env, name, chooser_filename);
 178 
 179     LOG_STRING_ARRAY(env, jFileNames);
 180     return result;
 181 }
 182 
 183 JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_gtk_GtkCommonDialogs__1showFolderChooser
 184   (JNIEnv *env, jclass clazz, jlong parent, jstring folder, jstring title) {
 185     (void)clazz;
 186 
 187     jstring jfilename = NULL;
 188     const char *chooser_folder;
 189     const char *chooser_title;
 190 
 191     if (!jstring_to_utf_get(env, folder, &chooser_folder)) {
 192         return NULL;
 193     }
 194 
 195     if (!jstring_to_utf_get(env, title, &chooser_title)) {
 196         jstring_to_utf_release(env, folder, chooser_folder);
 197         return NULL;
 198     }
 199 
 200     GtkWidget* chooser = gtk_file_chooser_dialog_new(
 201             chooser_title,
 202             gdk_window_handle_to_gtk(parent),
 203             GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
 204             GTK_STOCK_CANCEL,
 205             GTK_RESPONSE_CANCEL,
 206             GTK_STOCK_OPEN,
 207             GTK_RESPONSE_ACCEPT,
 208             NULL);
 209 
 210     if (chooser_folder != NULL) {
 211         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser),
 212                                             chooser_folder);
 213     }
 214 
 215     if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
 216         gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
 217         jfilename = env->NewStringUTF(filename);
 218         LOG1("Selected folder: %s\n", filename);
 219         g_free(filename);
 220     }
 221 
 222     jstring_to_utf_release(env, folder, chooser_folder);
 223     jstring_to_utf_release(env, title, chooser_title);
 224 
 225     gtk_widget_destroy(chooser);
 226     return jfilename;
 227 }
 228 
 229 } // extern "C"
 230 
 231 /**
 232  *
 233  * @param env
 234  * @param extFilters ExtensionFilter[]
 235  * @return
 236  */
 237 static GSList* setup_GtkFileFilters(GtkFileChooser* chooser, JNIEnv* env, jobjectArray extFilters, int default_filter_index) {
 238     int i;
 239     LOG0("Setup filters\n")
 240     //setup methodIDs
 241     jclass jcls = env->FindClass("com/sun/glass/ui/CommonDialogs$ExtensionFilter");
 242     if (EXCEPTION_OCCURED(env)) return NULL;
 243     jmethodID jgetDescription = env->GetMethodID(jcls,
 244                                          "getDescription", "()Ljava/lang/String;");
 245     if (EXCEPTION_OCCURED(env)) return NULL;
 246     jmethodID jextensionsToArray = env->GetMethodID(jcls,
 247                                          "extensionsToArray", "()[Ljava/lang/String;");
 248     if (EXCEPTION_OCCURED(env)) return NULL;
 249 
 250     jsize jfilters_size = env->GetArrayLength(extFilters);
 251     LOG1("Filters: %d\n", jfilters_size)
 252     if (jfilters_size == 0) return NULL;
 253 
 254     GSList* filter_list = NULL;
 255 
 256     for(i = 0; i<jfilters_size; i++) {
 257         GtkFileFilter* ffilter = gtk_file_filter_new();
 258         jobject jfilter = env->GetObjectArrayElement(extFilters, i);
 259         EXCEPTION_OCCURED(env);
 260 
 261         //setup description
 262         jstring jdesc = (jstring)env->CallObjectMethod(jfilter, jgetDescription);
 263         const char * description = env->GetStringUTFChars(jdesc, NULL);
 264         LOG2("description[%d]: %s\n", i, description)
 265         gtk_file_filter_set_name(ffilter, (gchar*)const_cast<char*>(description));
 266         env->ReleaseStringUTFChars(jdesc, description);
 267 
 268         //add patterns
 269         jobjectArray jextensions = (jobjectArray)env->CallObjectMethod(jfilter, jextensionsToArray);
 270         jsize jextarray_size = env->GetArrayLength(jextensions);
 271         LOG1("Patterns: %d\n", jextarray_size)
 272         int ext_idx;
 273         for(ext_idx = 0; ext_idx < jextarray_size; ext_idx++) {
 274             jstring jext = (jstring)env->GetObjectArrayElement(jextensions, ext_idx);
 275             EXCEPTION_OCCURED(env);
 276             const char * ext = env->GetStringUTFChars(jext, NULL);
 277             LOG2("pattern[%d]: %s\n", ext_idx, ext)
 278             gtk_file_filter_add_pattern(ffilter, (gchar*)const_cast<char*>(ext));
 279             env->ReleaseStringUTFChars(jext, ext);
 280         }
 281         LOG0("Filter ready\n")
 282         gtk_file_chooser_add_filter(chooser, ffilter);
 283 
 284         if (default_filter_index == i) {
 285             gtk_file_chooser_set_filter(chooser, ffilter);
 286         }
 287 
 288         filter_list = g_slist_append(filter_list, ffilter);
 289     }
 290     return filter_list;
 291 }