sun_awt_X11_GtkFileDialogPeer.c 11.7 KB
Newer Older
1
/*
2
 * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

26 27 28 29
#include <jni.h>
#include <stdio.h>
#include <jni_util.h>
#include <string.h>
30
#include "gtk_interface.h"
31
#include "sun_awt_X11_GtkFileDialogPeer.h"
32
#include "java_awt_FileDialog.h"
33
#include "debug_assert.h"
34

35
typedef void GtkWidget;
36 37 38 39 40
static JavaVM *jvm;

/* To cache some method IDs */
static jmethodID filenameFilterCallbackMethodID = NULL;
static jmethodID setFileInternalMethodID = NULL;
41 42 43 44 45 46 47 48
static jfieldID  widgetFieldID = NULL;

JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_initIDs
(JNIEnv *env, jclass cx)
{
    filenameFilterCallbackMethodID = (*env)->GetMethodID(env, cx,
            "filenameFilterCallback", "(Ljava/lang/String;)Z");
    DASSERT(filenameFilterCallbackMethodID != NULL);
49
    CHECK_NULL(filenameFilterCallbackMethodID);
50 51 52 53

    setFileInternalMethodID = (*env)->GetMethodID(env, cx,
            "setFileInternal", "(Ljava/lang/String;[Ljava/lang/String;)V");
    DASSERT(setFileInternalMethodID != NULL);
54
    CHECK_NULL(setFileInternalMethodID);
55 56 57 58

    widgetFieldID = (*env)->GetFieldID(env, cx, "widget", "J");
    DASSERT(widgetFieldID != NULL);
}
59 60 61 62 63 64 65 66 67

static gboolean filenameFilterCallback(const GtkFileFilterInfo * filter_info, gpointer obj)
{
    JNIEnv *env;
    jstring filename;

    env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);

    filename = (*env)->NewStringUTF(env, filter_info->filename);
68
    JNU_CHECK_EXCEPTION_RETURN(env, FALSE);
69 70 71 72 73

    return (*env)->CallBooleanMethod(env, obj, filenameFilterCallbackMethodID,
            filename);
}

74
static void quit(JNIEnv * env, jobject jpeer, gboolean isSignalHandler)
75
{
76 77 78 79 80
    jthrowable pendingException;
    if (pendingException = (*env)->ExceptionOccurred(env)) {
         (*env)->ExceptionClear(env);
    }

81 82 83
    GtkWidget * dialog = (GtkWidget*)jlong_to_ptr(
            (*env)->GetLongField(env, jpeer, widgetFieldID));

84 85
    if (dialog != NULL)
    {
86 87
        // Callbacks from GTK signals are made within the GTK lock
        // So, within a signal handler there is no need to call
88
        // gdk_threads_enter() / gtk->gdk_threads_leave()
89
        if (!isSignalHandler) {
90
            gtk->gdk_threads_enter();
91
        }
92

93 94
        gtk->gtk_widget_hide (dialog);
        gtk->gtk_widget_destroy (dialog);
95

96
        gtk->gtk_main_quit ();
97 98

        (*env)->SetLongField(env, jpeer, widgetFieldID, 0);
99

100
        if (!isSignalHandler) {
101
            gtk->gdk_threads_leave();
102
        }
103
    }
104 105 106 107

    if (pendingException) {
         (*env)->Throw(env, pendingException);
    }
108 109
}

110 111 112 113 114 115 116 117
/*
 * Class:     sun_awt_X11_GtkFileDialogPeer
 * Method:    quit
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_quit
(JNIEnv * env, jobject jpeer)
{
118
    quit(env, jpeer, FALSE);
119 120
}

121 122 123 124 125 126 127 128 129 130
/*
 * Class:     sun_awt_X11_GtkFileDialogPeer
 * Method:    toFront
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_toFront
(JNIEnv * env, jobject jpeer)
{
    GtkWidget * dialog;

131
    gtk->gdk_threads_enter();
132 133 134 135 136

    dialog = (GtkWidget*)jlong_to_ptr(
            (*env)->GetLongField(env, jpeer, widgetFieldID));

    if (dialog != NULL) {
137
        gtk->gtk_window_present((GtkWindow*)dialog);
138 139
    }

140
    gtk->gdk_threads_leave();
141 142
}

143 144 145 146 147 148 149 150 151 152
/*
 * Class:     sun_awt_X11_GtkFileDialogPeer
 * Method:    setBounds
 * Signature: (IIIII)V
 */
JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_setBounds
(JNIEnv * env, jobject jpeer, jint x, jint y, jint width, jint height, jint op)
{
    GtkWindow* dialog;

153
    gtk->gdk_threads_enter();
154 155 156 157 158 159

    dialog = (GtkWindow*)jlong_to_ptr(
        (*env)->GetLongField(env, jpeer, widgetFieldID));

    if (dialog != NULL) {
        if (x >= 0 && y >= 0) {
160
            gtk->gtk_window_move(dialog, (gint)x, (gint)y);
161 162
        }
        if (width > 0 && height > 0) {
163
            gtk->gtk_window_resize(dialog, (gint)width, (gint)height);
164 165 166
        }
    }

167
    gtk->gdk_threads_leave();
168 169
}

170 171
/*
 * baseDir should be freed by user.
172
 */
173
static gboolean isFromSameDirectory(GSList* list, gchar** baseDir) {
174

175 176 177
    GSList *it = list;
    gchar* prevDir = NULL;
    gboolean isAllDirsSame = TRUE;
178

179
    while (it) {
180
        gchar* dir = gtk->g_path_get_dirname((gchar*) it->data);
181

182 183
        if (prevDir && strcmp(prevDir, dir) != 0) {
            isAllDirsSame = FALSE;
184
            gtk->g_free(dir);
185 186
            break;
        }
187

188 189
        if (!prevDir) {
            prevDir = strdup(dir);
190
        }
191
        gtk->g_free(dir);
192 193

        it = it->next;
194 195
    }

196 197 198 199 200 201 202 203
    if (isAllDirsSame) {
        *baseDir = prevDir;
    } else {
        free(prevDir);
        *baseDir = strdup("/");
    }

    return isAllDirsSame;
204 205
}

206
/**
207
 * Convert a GSList to an array of filenames
208
 */
209
static jobjectArray toFilenamesArray(JNIEnv *env, GSList* list, jstring* jcurrent_folder)
210 211 212 213 214 215
{
    jstring str;
    jclass stringCls;
    GSList *iterator;
    jobjectArray array;
    int i;
216 217 218
    gchar* entry;
    gchar * baseDir;
    gboolean isFromSameDir;
219 220 221 222 223 224 225

    if (list == NULL) {
        return NULL;
    }

    stringCls = (*env)->FindClass(env, "java/lang/String");
    if (stringCls == NULL) {
226
        (*env)->ExceptionClear(env);
227 228 229 230
        JNU_ThrowInternalError(env, "Could not get java.lang.String class");
        return NULL;
    }

231
    array = (*env)->NewObjectArray(env, gtk->gtk_g_slist_length(list), stringCls, NULL);
232
    if (array == NULL) {
233
        (*env)->ExceptionClear(env);
234 235 236 237
        JNU_ThrowInternalError(env, "Could not instantiate array files array");
        return NULL;
    }

238
    isFromSameDir = isFromSameDirectory(list, &baseDir);
239

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    *jcurrent_folder = (*env)->NewStringUTF(env, baseDir);
    if (*jcurrent_folder == NULL) {
        free(baseDir);
        return NULL;
    }

    for (iterator = list, i=0;
            iterator;
            iterator = iterator->next, i++) {

        entry = (gchar*) iterator->data;

        if (isFromSameDir) {
            entry = strrchr(entry, '/') + 1;
        } else if (entry[0] == '/') {
255 256 257 258
            entry++;
        }

        str = (*env)->NewStringUTF(env, entry);
259 260 261 262
        if((*env)->ExceptionCheck(env)){
            break;
        }
        if (str) {
263
            (*env)->SetObjectArrayElement(env, array, i, str);
264 265 266
            if((*env)->ExceptionCheck(env)){
                break;
            }
267
        }
268 269
    }

270
    free(baseDir);
271 272 273
    return array;
}

274 275 276 277
static void handle_response(GtkWidget* aDialog, gint responseId, gpointer obj)
{
    JNIEnv *env;
    GSList *filenames;
278
    jstring jcurrent_folder = NULL;
279 280 281 282 283 284
    jobjectArray jfilenames;

    env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
    filenames = NULL;

    if (responseId == GTK_RESPONSE_ACCEPT) {
285
        filenames = gtk->gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(aDialog));
286
    }
287 288 289

    jfilenames = toFilenamesArray(env, filenames, &jcurrent_folder);

290 291 292
    if (!(*env)->ExceptionCheck(env)) {
        (*env)->CallVoidMethod(env, obj, setFileInternalMethodID,
                               jcurrent_folder, jfilenames);
293
    }
294

295
    quit(env, (jobject)obj, TRUE);
296 297 298 299 300
}

/*
 * Class:     sun_awt_X11_GtkFileDialogPeer
 * Method:    run
301
 * Signature: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/io/FilenameFilter;ZII)V
302 303 304 305
 */
JNIEXPORT void JNICALL
Java_sun_awt_X11_GtkFileDialogPeer_run(JNIEnv * env, jobject jpeer,
        jstring jtitle, jint mode, jstring jdir, jstring jfile,
306
        jobject jfilter, jboolean multiple, int x, int y)
307
{
308
    GtkWidget *dialog = NULL;
309 310 311 312
    GtkFileFilter *filter;

    if (jvm == NULL) {
        (*env)->GetJavaVM(env, &jvm);
313
        JNU_CHECK_EXCEPTION(env);
314 315
    }

316
    gtk->gdk_threads_enter();
317

318
    const char *title = jtitle == NULL? "": (*env)->GetStringUTFChars(env, jtitle, 0);
319 320 321 322 323
    if (title == NULL) {
        (*env)->ExceptionClear(env);
        JNU_ThrowOutOfMemoryError(env, "Could not get title");
        return;
    }
324

325
    if (mode == java_awt_FileDialog_SAVE) {
326
        /* Save action */
327
        dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
328 329 330 331 332
                GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
                GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
    }
    else {
        /* Default action OPEN */
333
        dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
334 335 336 337 338
                GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
                GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);

        /* Set multiple selection mode, that is allowed only in OPEN action */
        if (multiple) {
339
            gtk->gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),
340 341 342 343
                    multiple);
        }
    }

344 345 346
    if (jtitle != NULL) {
      (*env)->ReleaseStringUTFChars(env, jtitle, title);
    }
347 348 349 350

    /* Set the directory */
    if (jdir != NULL) {
        const char *dir = (*env)->GetStringUTFChars(env, jdir, 0);
351 352 353 354 355
        if (dir == NULL) {
            (*env)->ExceptionClear(env);
            JNU_ThrowOutOfMemoryError(env, "Could not get dir");
            return;
        }
356
        gtk->gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir);
357 358 359 360 361 362
        (*env)->ReleaseStringUTFChars(env, jdir, dir);
    }

    /* Set the filename */
    if (jfile != NULL) {
        const char *filename = (*env)->GetStringUTFChars(env, jfile, 0);
363 364 365 366 367
        if (filename == NULL) {
            (*env)->ExceptionClear(env);
            JNU_ThrowOutOfMemoryError(env, "Could not get filename");
            return;
        }
368
        if (mode == java_awt_FileDialog_SAVE) {
369
            gtk->gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
370
        } else {
371
            gtk->gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), filename);
372
        }
373 374 375 376 377
        (*env)->ReleaseStringUTFChars(env, jfile, filename);
    }

    /* Set the file filter */
    if (jfilter != NULL) {
378 379
        filter = gtk->gtk_file_filter_new();
        gtk->gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
380
                filenameFilterCallback, jpeer, NULL);
381
        gtk->gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
382 383 384
    }

    /* Other Properties */
385 386 387
    if (gtk->gtk_check_version(2, 8, 0) == NULL ||
                                     gtk->gtk_check_version(3, 0, 0) == NULL) {
        gtk->gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(
388 389 390
                dialog), TRUE);
    }

391 392
    /* Set the initial location */
    if (x >= 0 && y >= 0) {
393
        gtk->gtk_window_move((GtkWindow*)dialog, (gint)x, (gint)y);
394 395 396 397 398

        // NOTE: it doesn't set the initial size for the file chooser
        // as it seems like the file chooser overrides the size internally
    }

399 400
    gtk->g_signal_connect_data(dialog, "response", G_CALLBACK(
            handle_response), jpeer, 0, 0);
401 402 403

    (*env)->SetLongField(env, jpeer, widgetFieldID, ptr_to_jlong(dialog));

404
    gtk->gtk_widget_show(dialog);
405

406 407
    gtk->gtk_main();
    gtk->gdk_threads_leave();
408
}
409