From 303c220d3a27d37e5e0a431a9a391d1c8e38f2ed Mon Sep 17 00:00:00 2001 From: Andrej Kacian Date: Sat, 27 Aug 2016 19:51:14 +0200 Subject: [PATCH] Allow multiple file selection in Windows native file chooser. --- src/gtk/w32_filesel.c | 256 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 243 insertions(+), 13 deletions(-) diff --git a/src/gtk/w32_filesel.c b/src/gtk/w32_filesel.c index 0218e6e4d..64ef243c1 100644 --- a/src/gtk/w32_filesel.c +++ b/src/gtk/w32_filesel.c @@ -140,7 +140,7 @@ gchar *filesel_select_file_open(const gchar *title, const gchar *path) o.lpstrFileTitle = NULL; o.lpstrInitialDir = path16; o.lpstrTitle = title16; - o.Flags = OFN_LONGNAMES; + o.Flags = OFN_LONGNAMES | OFN_EXPLORER; ctx = g_new0(WinChooserCtx, 1); ctx->data = &o; @@ -185,15 +185,110 @@ gchar *filesel_select_file_open(const gchar *title, const gchar *path) return str; } -/* TODO: Allow selecting of multiple files with OFN_ALLOWMULTISELECT - * flag and parsing the long string with returned file names. */ GList *filesel_select_multiple_files_open(const gchar *title) { GList *file_list = NULL; - gchar *ret = filesel_select_file_open(title, NULL); + gboolean ret; + gunichar2 *title16; + glong conv_items; + GError *error = NULL; + WinChooserCtx *ctx; +#ifdef USE_PTHREAD + pthread_t pt; +#endif + + /* Chooser dialog title needs to be UTF-16 as well. */ + title16 = g_utf8_to_utf16(title, -1, NULL, NULL, &error); + if (error != NULL) { + debug_print("dialog title '%s' conversion to UTF-16 failed\n", title); + g_error_free(error); + } + + o.lStructSize = sizeof(OPENFILENAME); + if (focus_window != NULL) + o.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(focus_window)); + else + o.hwndOwner = NULL; + o.hInstance = NULL; + o.lpstrFilter = NULL; + o.lpstrCustomFilter = NULL; + o.nFilterIndex = 0; + o.lpstrFile = g_malloc0(MAXPATHLEN); + o.nMaxFile = MAXPATHLEN; + o.lpstrFileTitle = NULL; + o.lpstrTitle = title16; + o.Flags = OFN_LONGNAMES | OFN_EXPLORER | OFN_ALLOWMULTISELECT; + + ctx = g_new0(WinChooserCtx, 1); + ctx->data = &o; + ctx->done = FALSE; + +#ifdef USE_PTHREAD + if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, threaded_GetOpenFileName, + (void *)ctx) != 0) { + debug_print("Couldn't run in a thread, continuing unthreaded.\n"); + threaded_GetOpenFileName(ctx); + } else { + while (!ctx->done) { + claws_do_idle(); + } + pthread_join(pt, NULL); + } + ret = ctx->return_value; +#else + debug_print("No threads available, continuing unthreaded.\n"); + ret = GetOpenFileName(&o); +#endif - if (ret != NULL) - file_list = g_list_append(file_list, ret); + g_free(title16); + g_free(ctx); + + if (!ret) { + g_free(o.lpstrFile); + return NULL; + } + + /* Now convert the returned file path back from UTF-16. */ + gchar *dir = g_utf16_to_utf8(o.lpstrFile, o.nMaxFile, NULL, &conv_items, &error); + if (error != NULL) { + alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"), + error->message); + debug_print("returned file path conversion to UTF-8 failed\n"); + g_error_free(error); + } + + gunichar2 *f = o.lpstrFile + g_utf8_strlen(dir, -1) + 1; + + do { + gchar *file = g_utf16_to_utf8(f, o.nMaxFile, NULL, &conv_items, &error); + if (error != NULL) { + alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"), + error->message); + debug_print("returned file path conversion to UTF-8 failed\n"); + g_error_free(error); + } + + if (file == NULL || strlen(file) == 0) { + g_free(file); + break; + } + + debug_print("Selected file '%s%c%s'\n", + dir, G_DIR_SEPARATOR, file); + file_list = g_list_append(file_list, + g_strconcat(dir, G_DIR_SEPARATOR_S, file, NULL)); + + f = f + g_utf8_strlen(file, -1) + 1; + g_free(file); + } while (TRUE); + + if (file_list == NULL) { + debug_print("Selected single file '%s'\n", dir); + file_list = g_list_append(file_list, dir); + } else { + g_free(dir); + } + g_free(o.lpstrFile); return file_list; } @@ -244,7 +339,7 @@ gchar *filesel_select_file_open_with_filter(const gchar *title, const gchar *pat o.lpstrFileTitle = NULL; o.lpstrInitialDir = path16; o.lpstrTitle = title16; - o.Flags = OFN_LONGNAMES; + o.Flags = OFN_LONGNAMES | OFN_EXPLORER; if (filter != NULL && strlen(filter) > 0) { debug_print("Setting filter '%s'\n", filter); @@ -315,16 +410,151 @@ gchar *filesel_select_file_open_with_filter(const gchar *title, const gchar *pat return str; } -/* TODO: Allow selecting of multiple files with OFN_ALLOWMULTISELECT - * flag and parsing the long string with returned file names. */ GList *filesel_select_multiple_files_open_with_filter(const gchar *title, const gchar *path, const gchar *filter) { GList *file_list = NULL; - gchar *ret = filesel_select_file_open_with_filter(title, path, filter); + gboolean ret; + gchar *win_filter16 = NULL; + gunichar2 *path16, *title16, *filter16; + glong conv_items; + guint sz; + GError *error = NULL; + WinChooserCtx *ctx; +#ifdef USE_PTHREAD + pthread_t pt; +#endif + + /* Path needs to be converted to UTF-16, so that the native chooser + * can understand it. */ + path16 = g_utf8_to_utf16(path, -1, NULL, NULL, &error); + if (error != NULL) { + alertpanel_error(_("Could not convert file path to UTF-16:\n\n%s"), + error->message); + debug_print("file path '%s' conversion to UTF-16 failed\n", path); + g_error_free(error); + return NULL; + } - if (ret != NULL) - file_list = g_list_append(file_list, ret); + /* Chooser dialog title needs to be UTF-16 as well. */ + title16 = g_utf8_to_utf16(title, -1, NULL, NULL, &error); + if (error != NULL) { + debug_print("dialog title '%s' conversion to UTF-16 failed\n", title); + g_error_free(error); + } + + o.lStructSize = sizeof(OPENFILENAME); + if (focus_window != NULL) + o.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(focus_window)); + else + o.hwndOwner = NULL; + o.lpstrFilter = NULL; + o.lpstrCustomFilter = NULL; + o.nFilterIndex = 0; + o.lpstrFile = g_malloc0(MAXPATHLEN); + o.nMaxFile = MAXPATHLEN; + o.lpstrFileTitle = NULL; + o.lpstrInitialDir = path16; + o.lpstrTitle = title16; + o.Flags = OFN_LONGNAMES | OFN_EXPLORER | OFN_ALLOWMULTISELECT; + + if (filter != NULL && strlen(filter) > 0) { + debug_print("Setting filter '%s'\n", filter); + filter16 = g_utf8_to_utf16(filter, -1, NULL, &conv_items, &error); + /* We're creating a UTF16 (2 bytes for each character) string: + * "filter\0filter\0\0" + * As g_utf8_to_utf16() will stop on first null byte, even if + * we pass string length in its second argument, we have to + * construct this string manually. + * conv_items contains number of UTF16 characters of our filter. + * Therefore we need enough bytes to store the filter string twice + * and three null chars. */ + sz = sizeof(gunichar2); + win_filter16 = g_malloc0(conv_items*sz*2 + sz*3); + memcpy(win_filter16, filter16, conv_items*sz); + memcpy(win_filter16 + conv_items*sz + sz, filter16, conv_items*sz); + g_free(filter16); + + if (error != NULL) { + debug_print("dialog title '%s' conversion to UTF-16 failed\n", title); + g_error_free(error); + } + o.lpstrFilter = (LPCTSTR)win_filter16; + o.nFilterIndex = 1; + } + + ctx = g_new0(WinChooserCtx, 1); + ctx->data = &o; + ctx->done = FALSE; + +#ifdef USE_PTHREAD + if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, threaded_GetOpenFileName, + (void *)ctx) != 0) { + debug_print("Couldn't run in a thread, continuing unthreaded.\n"); + threaded_GetOpenFileName(ctx); + } else { + while (!ctx->done) { + claws_do_idle(); + } + pthread_join(pt, NULL); + } + ret = ctx->return_value; +#else + debug_print("No threads available, continuing unthreaded.\n"); + ret = GetOpenFileName(&o); +#endif + + g_free(win_filter16); + g_free(path16); + g_free(title16); + g_free(ctx); + + if (!ret) { + g_free(o.lpstrFile); + return NULL; + } + + /* Now convert the returned file path back from UTF-16. */ + gchar *dir = g_utf16_to_utf8(o.lpstrFile, o.nMaxFile, NULL, &conv_items, &error); + if (error != NULL) { + alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"), + error->message); + debug_print("returned file path conversion to UTF-8 failed\n"); + g_error_free(error); + } + + gunichar2 *f = o.lpstrFile + g_utf8_strlen(dir, -1) + 1; + + do { + gchar *file = g_utf16_to_utf8(f, o.nMaxFile, NULL, &conv_items, &error); + if (error != NULL) { + alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"), + error->message); + debug_print("returned file path conversion to UTF-8 failed\n"); + g_error_free(error); + } + + if (file == NULL || strlen(file) == 0) { + g_free(file); + break; + } + + debug_print("Selected file '%s%c%s'\n", + dir, G_DIR_SEPARATOR, file); + file_list = g_list_append(file_list, + g_strconcat(dir, G_DIR_SEPARATOR_S, file, NULL)); + + f = f + g_utf8_strlen(file, -1) + 1; + g_free(file); + } while (TRUE); + + if (file_list == NULL) { + debug_print("Selected single file '%s'\n", dir); + file_list = g_list_append(file_list, dir); + } else { + g_free(dir); + } + g_free(o.lpstrFile); return file_list; } @@ -393,7 +623,7 @@ gchar *filesel_select_file_save(const gchar *title, const gchar *path) o.lpstrFileTitle = NULL; o.lpstrInitialDir = path16; o.lpstrTitle = title16; - o.Flags = OFN_LONGNAMES; + o.Flags = OFN_LONGNAMES | OFN_EXPLORER; ctx = g_new0(WinChooserCtx, 1); ctx->data = &o; -- 2.25.1