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 = glass_file_chooser_dialog(
 116             chooser_title,
 117             gdk_window_handle_to_gtk(parent),
 118             static_cast<GtkFileChooserAction>(chooser_type),
 119             (chooser_type == GTK_FILE_CHOOSER_ACTION_OPEN ? GTK_STOCK_OPEN : GTK_STOCK_SAVE)
 120             );
 121 
 122     if (chooser_type == GTK_FILE_CHOOSER_ACTION_SAVE) {
 123         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser), chooser_filename);
 124         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (chooser), TRUE);
 125     }
 126 
 127     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), (JNI_TRUE == multiple));
 128     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), chooser_folder);
 129     GSList* filters = setup_GtkFileFilters(GTK_FILE_CHOOSER(chooser), env, jFilters, default_filter_index);
 130 
 131     if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
 132         GSList* fnames_gslist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chooser));
 133         guint fnames_list_len = g_slist_length(fnames_gslist);
 134         LOG1("FileChooser selected files: %d\n", fnames_list_len)
 135 
 136         if (fnames_list_len > 0) {
 137             jFileNames = env->NewObjectArray((jsize)fnames_list_len, jStringCls, NULL);
 138             EXCEPTION_OCCURED(env);
 139             for (guint i = 0; i < fnames_list_len; i++) {
 140                 filename = (char*)g_slist_nth(fnames_gslist, i)->data;
 141                 LOG1("Add [%s] into returned filenames\n", filename)
 142                 jfilename = env->NewStringUTF(filename);
 143                 EXCEPTION_OCCURED(env);
 144                 env->SetObjectArrayElement(jFileNames, (jsize)i, jfilename);
 145                 EXCEPTION_OCCURED(env);
 146             }
 147             g_slist_foreach(fnames_gslist, (GFunc) free_fname, NULL);
 148             g_slist_free(fnames_gslist);
 149         }
 150     }
 151 
 152     if (!jFileNames) {
 153         jFileNames = env->NewObjectArray(0, jStringCls, NULL);
 154         EXCEPTION_OCCURED(env);
 155     }
 156 
 157     int index = g_slist_index(filters, gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(chooser)));
 158 
 159     jclass jCommonDialogs = (jclass) env->FindClass("com/sun/glass/ui/CommonDialogs");
 160     EXCEPTION_OCCURED(env);
 161     jmethodID jCreateFileChooserResult = env->GetStaticMethodID(jCommonDialogs,
 162             "createFileChooserResult",
 163             "([Ljava/lang/String;[Lcom/sun/glass/ui/CommonDialogs$ExtensionFilter;I)Lcom/sun/glass/ui/CommonDialogs$FileChooserResult;");
 164 
 165     EXCEPTION_OCCURED(env);
 166 
 167     jobject result =
 168             env->CallStaticObjectMethod(jCommonDialogs, jCreateFileChooserResult, jFileNames, jFilters, index);
 169     LOG_EXCEPTION(env)
 170 
 171     g_slist_free(filters);
 172     gtk_widget_destroy(chooser);
 173 
 174     jstring_to_utf_release(env, folder, chooser_folder);
 175     jstring_to_utf_release(env, title, chooser_title);
 176     jstring_to_utf_release(env, name, chooser_filename);
 177 
 178     LOG_STRING_ARRAY(env, jFileNames);
 179     return result;
 180 }
 181 
 182 JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_gtk_GtkCommonDialogs__1showFolderChooser
 183   (JNIEnv *env, jclass clazz, jlong parent, jstring folder, jstring title) {
 184     (void)clazz;
 185 
 186     jstring jfilename = NULL;
 187     const char *chooser_folder;
 188     const char *chooser_title;
 189 
 190     if (!jstring_to_utf_get(env, folder, &chooser_folder)) {
 191         return NULL;
 192     }
 193 
 194     if (!jstring_to_utf_get(env, title, &chooser_title)) {
 195         jstring_to_utf_release(env, folder, chooser_folder);
 196         return NULL;
 197     }
 198 
 199     GtkWidget* chooser = glass_file_chooser_dialog(
 200             chooser_title,
 201             gdk_window_handle_to_gtk(parent),
 202             GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
 203             GTK_STOCK_OPEN
 204             );
 205 
 206     if (chooser_folder != NULL) {
 207         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser),
 208                                             chooser_folder);
 209     }
 210 
 211     if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
 212         gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
 213         jfilename = env->NewStringUTF(filename);
 214         LOG1("Selected folder: %s\n", filename);
 215         g_free(filename);
 216     }
 217 
 218     jstring_to_utf_release(env, folder, chooser_folder);
 219     jstring_to_utf_release(env, title, chooser_title);
 220 
 221     gtk_widget_destroy(chooser);
 222     return jfilename;
 223 }
 224 
 225 } // extern "C"
 226 
 227 /**
 228  *
 229  * @param env
 230  * @param extFilters ExtensionFilter[]
 231  * @return
 232  */
 233 static GSList* setup_GtkFileFilters(GtkFileChooser* chooser, JNIEnv* env, jobjectArray extFilters, int default_filter_index) {
 234     int i;
 235     LOG0("Setup filters\n")
 236     //setup methodIDs
 237     jclass jcls = env->FindClass("com/sun/glass/ui/CommonDialogs$ExtensionFilter");
 238     if (EXCEPTION_OCCURED(env)) return NULL;
 239     jmethodID jgetDescription = env->GetMethodID(jcls,
 240                                          "getDescription", "()Ljava/lang/String;");
 241     if (EXCEPTION_OCCURED(env)) return NULL;
 242     jmethodID jextensionsToArray = env->GetMethodID(jcls,
 243                                          "extensionsToArray", "()[Ljava/lang/String;");
 244     if (EXCEPTION_OCCURED(env)) return NULL;
 245 
 246     jsize jfilters_size = env->GetArrayLength(extFilters);
 247     LOG1("Filters: %d\n", jfilters_size)
 248     if (jfilters_size == 0) return NULL;
 249 
 250     GSList* filter_list = NULL;
 251 
 252     for(i = 0; i<jfilters_size; i++) {
 253         GtkFileFilter* ffilter = gtk_file_filter_new();
 254         jobject jfilter = env->GetObjectArrayElement(extFilters, i);
 255         EXCEPTION_OCCURED(env);
 256 
 257         //setup description
 258         jstring jdesc = (jstring)env->CallObjectMethod(jfilter, jgetDescription);
 259         const char * description = env->GetStringUTFChars(jdesc, NULL);
 260         LOG2("description[%d]: %s\n", i, description)
 261         gtk_file_filter_set_name(ffilter, (gchar*)const_cast<char*>(description));
 262         env->ReleaseStringUTFChars(jdesc, description);
 263 
 264         //add patterns
 265         jobjectArray jextensions = (jobjectArray)env->CallObjectMethod(jfilter, jextensionsToArray);
 266         jsize jextarray_size = env->GetArrayLength(jextensions);
 267         LOG1("Patterns: %d\n", jextarray_size)
 268         int ext_idx;
 269         for(ext_idx = 0; ext_idx < jextarray_size; ext_idx++) {
 270             jstring jext = (jstring)env->GetObjectArrayElement(jextensions, ext_idx);
 271             EXCEPTION_OCCURED(env);
 272             const char * ext = env->GetStringUTFChars(jext, NULL);
 273             LOG2("pattern[%d]: %s\n", ext_idx, ext)
 274             gtk_file_filter_add_pattern(ffilter, (gchar*)const_cast<char*>(ext));
 275             env->ReleaseStringUTFChars(jext, ext);
 276         }
 277         LOG0("Filter ready\n")
 278         gtk_file_chooser_add_filter(chooser, ffilter);
 279 
 280         if (default_filter_index == i) {
 281             gtk_file_chooser_set_filter(chooser, ffilter);
 282         }
 283 
 284         filter_list = g_slist_append(filter_list, ffilter);
 285     }
 286     return filter_list;
 287 }