2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2016 The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
29 #include <gdk/gdkwin32.h>
36 #include "alertpanel.h"
37 #include "manage_window.h"
40 static OPENFILENAME o;
43 /* Since running the native dialogs in the same thread stops GTK+
44 * loop from redrawing other windows on the background, we need
45 * to run the dialogs in a separate thread. */
47 /* TODO: There's a lot of code repeat in this file, it could be
48 * refactored to be neater. */
50 struct _WinChooserCtx {
52 gboolean return_value;
53 PIDLIST_ABSOLUTE return_value_pidl;
57 typedef struct _WinChooserCtx WinChooserCtx;
59 static void *threaded_GetOpenFileName(void *arg)
61 WinChooserCtx *ctx = (WinChooserCtx *)arg;
63 g_return_val_if_fail(ctx != NULL, NULL);
64 g_return_val_if_fail(ctx->data != NULL, NULL);
66 ctx->return_value = GetOpenFileName(ctx->data);
72 static void *threaded_GetSaveFileName(void *arg)
74 WinChooserCtx *ctx = (WinChooserCtx *)arg;
76 g_return_val_if_fail(ctx != NULL, NULL);
77 g_return_val_if_fail(ctx->data != NULL, NULL);
79 ctx->return_value = GetSaveFileName(ctx->data);
85 static void *threaded_SHBrowseForFolder(void *arg)
87 WinChooserCtx *ctx = (WinChooserCtx *)arg;
89 g_return_val_if_fail(ctx != NULL, NULL);
90 g_return_val_if_fail(ctx->data != NULL, NULL);
92 ctx->return_value_pidl = SHBrowseForFolder(ctx->data);
99 gchar *filesel_select_file_open(const gchar *title, const gchar *path)
103 gunichar2 *path16, *title16;
105 GError *error = NULL;
111 /* Path needs to be converted to UTF-16, so that the native chooser
112 * can understand it. */
113 path16 = g_utf8_to_utf16(path, -1, NULL, &conv_items, &error);
115 alertpanel_error(_("Could not convert file path to UTF-16:\n\n%s"),
117 debug_print("file path '%s' conversion to UTF-16 failed\n", path);
122 /* Chooser dialog title needs to be UTF-16 as well. */
123 title16 = g_utf8_to_utf16(title, -1, NULL, NULL, &error);
125 debug_print("dialog title '%s' conversion to UTF-16 failed\n", title);
129 o.lStructSize = sizeof(OPENFILENAME);
130 if (focus_window != NULL)
131 o.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(focus_window));
135 o.lpstrFilter = NULL;
136 o.lpstrCustomFilter = NULL;
138 o.lpstrFile = g_malloc0(MAXPATHLEN);
139 o.nMaxFile = MAXPATHLEN;
140 o.lpstrFileTitle = NULL;
141 o.lpstrInitialDir = path16;
142 o.lpstrTitle = title16;
143 o.Flags = OFN_LONGNAMES;
145 ctx = g_new0(WinChooserCtx, 1);
150 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, threaded_GetOpenFileName,
152 debug_print("Couldn't run in a thread, continuing unthreaded.\n");
153 threaded_GetOpenFileName(ctx);
158 pthread_join(pt, NULL);
160 ret = ctx->return_value;
162 debug_print("No threads available, continuing unthreaded.\n");
163 ret = GetOpenFileName(&o);
175 /* Now convert the returned file path back from UTF-16. */
176 str = g_utf16_to_utf8(o.lpstrFile, o.nMaxFile, NULL, NULL, &error);
178 alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"),
180 debug_print("returned file path conversion to UTF-8 failed\n");
188 /* TODO: Allow selecting of multiple files with OFN_ALLOWMULTISELECT
189 * flag and parsing the long string with returned file names. */
190 GList *filesel_select_multiple_files_open(const gchar *title)
192 GList *file_list = NULL;
193 gchar *ret = filesel_select_file_open(title, NULL);
196 file_list = g_list_append(file_list, ret);
201 gchar *filesel_select_file_open_with_filter(const gchar *title, const gchar *path,
205 gchar *win_filter = NULL, *str;
206 gunichar2 *path16, *title16, *win_filter16 = NULL;
208 GError *error = NULL;
214 /* Path needs to be converted to UTF-16, so that the native chooser
215 * can understand it. */
216 path16 = g_utf8_to_utf16(path, -1, NULL, &conv_items, &error);
218 alertpanel_error(_("Could not convert file path to UTF-16:\n\n%s"),
220 debug_print("file path '%s' conversion to UTF-16 failed\n", path);
225 /* Chooser dialog title needs to be UTF-16 as well. */
226 title16 = g_utf8_to_utf16(title, -1, NULL, NULL, &error);
228 debug_print("dialog title '%s' conversion to UTF-16 failed\n", title);
232 o.lStructSize = sizeof(OPENFILENAME);
233 if (focus_window != NULL)
234 o.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(focus_window));
237 o.lpstrFilter = NULL;
238 o.lpstrCustomFilter = NULL;
240 o.lpstrFile = g_malloc0(MAXPATHLEN);
241 o.nMaxFile = MAXPATHLEN;
242 o.lpstrFileTitle = NULL;
243 o.lpstrInitialDir = path16;
244 o.lpstrTitle = title16;
245 o.Flags = OFN_LONGNAMES;
247 if (filter != NULL && strlen(filter) > 0) {
248 win_filter = g_strdup_printf("%s%c%s%c%c",
249 filter, '\0', filter, '\0', '\0');
250 win_filter16 = g_utf8_to_utf16(win_filter, -1, NULL, NULL, &error);
253 debug_print("dialog title '%s' conversion to UTF-16 failed\n", title);
256 o.lpstrFilter = win_filter16;
260 ctx = g_new0(WinChooserCtx, 1);
265 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, threaded_GetOpenFileName,
267 debug_print("Couldn't run in a thread, continuing unthreaded.\n");
268 threaded_GetOpenFileName(ctx);
273 pthread_join(pt, NULL);
275 ret = ctx->return_value;
277 debug_print("No threads available, continuing unthreaded.\n");
278 ret = GetOpenFileName(&o);
281 g_free(win_filter16);
291 /* Now convert the returned file path back from UTF-16. */
292 str = g_utf16_to_utf8(o.lpstrFile, o.nMaxFile, NULL, NULL, &error);
294 alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"),
296 debug_print("returned file path conversion to UTF-8 failed\n");
304 /* TODO: Allow selecting of multiple files with OFN_ALLOWMULTISELECT
305 * flag and parsing the long string with returned file names. */
306 GList *filesel_select_multiple_files_open_with_filter(const gchar *title,
307 const gchar *path, const gchar *filter)
309 GList *file_list = NULL;
310 gchar *ret = filesel_select_file_open_with_filter(title, path, filter);
313 file_list = g_list_append(file_list, ret);
318 gchar *filesel_select_file_save(const gchar *title, const gchar *path)
321 gchar *str, *filename = NULL;
322 gunichar2 *filename16, *path16, *title16;
324 GError *error = NULL;
330 /* Find the filename part, if any */
331 if (path[strlen(path)-1] == G_DIR_SEPARATOR) {
333 } else if ((filename = strrchr(path, G_DIR_SEPARATOR)) != NULL) {
336 filename = (char *) path;
339 /* Convert it to UTF-16. */
340 filename16 = g_utf8_to_utf16(filename, -1, NULL, &conv_items, &error);
342 alertpanel_error(_("Could not convert attachment name to UTF-16:\n\n%s"),
344 debug_print("filename '%s' conversion to UTF-16 failed\n", filename);
349 /* Path needs to be converted to UTF-16, so that the native chooser
350 * can understand it. */
351 path16 = g_utf8_to_utf16(path, -1, NULL, NULL, &error);
353 alertpanel_error(_("Could not convert file path to UTF-16:\n\n%s"),
355 debug_print("file path '%s' conversion to UTF-16 failed\n", path);
361 /* Chooser dialog title needs to be UTF-16 as well. */
362 title16 = g_utf8_to_utf16(title, -1, NULL, NULL, &error);
364 debug_print("dialog title '%s' conversion to UTF-16 failed\n", title);
368 o.lStructSize = sizeof(OPENFILENAME);
369 if (focus_window != NULL)
370 o.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(focus_window));
373 o.lpstrFilter = NULL;
374 o.lpstrCustomFilter = NULL;
375 o.lpstrFile = g_malloc0(MAXPATHLEN);
377 memcpy(o.lpstrFile, filename16, conv_items * sizeof(gunichar2));
378 o.nMaxFile = MAXPATHLEN;
379 o.lpstrFileTitle = NULL;
380 o.lpstrInitialDir = path16;
381 o.lpstrTitle = title16;
382 o.Flags = OFN_LONGNAMES;
384 ctx = g_new0(WinChooserCtx, 1);
386 ctx->return_value = FALSE;
390 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, threaded_GetSaveFileName,
392 debug_print("Couldn't run in a thread, continuing unthreaded.\n");
393 threaded_GetSaveFileName(ctx);
398 pthread_join(pt, NULL);
400 ret = ctx->return_value;
402 debug_print("No threads available, continuing unthreaded.\n");
403 ret = GetSaveFileName(&o);
416 /* Now convert the returned file path back from UTF-16. */
417 str = g_utf16_to_utf8(o.lpstrFile, o.nMaxFile, NULL, NULL, &error);
419 alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"),
421 debug_print("returned file path conversion to UTF-8 failed\n");
429 gchar *filesel_select_file_open_folder(const gchar *title, const gchar *path)
431 PIDLIST_ABSOLUTE pidl;
433 gunichar2 *path16, *title16;
435 PIDLIST_ABSOLUTE ppidl;
436 GError *error = NULL;
442 /* Path needs to be converted to UTF-16, so that the native chooser
443 * can understand it. */
444 path16 = g_utf8_to_utf16(path, -1, NULL, &conv_items, &error);
446 alertpanel_error(_("Could not convert file path to UTF-16:\n\n%s"),
448 debug_print("file path '%s' conversion to UTF-16 failed\n", path);
452 /* Get a PIDL_ABSOLUTE for b.pidlRoot. */
453 if (SHParseDisplayName(path16, NULL, &ppidl, 0, NULL) == S_OK) {
459 /* Chooser dialog title needs to be UTF-16 as well. */
460 title16 = g_utf8_to_utf16(title, -1, NULL, NULL, &error);
462 debug_print("dialog title '%s' conversion to UTF-16 failed\n", title);
466 if (focus_window != NULL)
467 b.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(focus_window));
470 b.pszDisplayName = g_malloc(MAXPATHLEN);
471 b.lpszTitle = title16;
477 ctx = g_new0(WinChooserCtx, 1);
482 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, threaded_SHBrowseForFolder,
484 debug_print("Couldn't run in a thread, continuing unthreaded.\n");
485 threaded_SHBrowseForFolder(ctx);
490 pthread_join(pt, NULL);
492 pidl = ctx->return_value_pidl;
494 debug_print("No threads available, continuing unthreaded.\n");
495 pidl = SHBrowseForFolder(&b);
500 g_free(b.pszDisplayName);
507 /* Now convert the returned file path back from UTF-16. */
508 /* Unfortunately, there is no field in BROWSEINFO struct to indicate
509 * actual length of string in pszDisplayName, so we have to assume
510 * the string is null-terminated. */
511 str = g_utf16_to_utf8(b.pszDisplayName, -1, NULL, NULL, &error);
513 alertpanel_error(_("Could not convert file path back to UTF-8:\n\n%s"),
515 debug_print("returned file path conversion to UTF-8 failed\n");
518 g_free(b.pszDisplayName);
527 gchar *filesel_select_file_save_folder(const gchar *title, const gchar *path)
529 return filesel_select_file_open_folder(title, path);