summaryrefslogtreecommitdiff
path: root/src/filesel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/filesel.c')
-rw-r--r--src/filesel.c611
1 files changed, 611 insertions, 0 deletions
diff --git a/src/filesel.c b/src/filesel.c
new file mode 100644
index 0000000..e4e3ecd
--- /dev/null
+++ b/src/filesel.c
@@ -0,0 +1,611 @@
1/*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
4 *
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 2 of the License, or
8 * (at your option) any later version.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <glib.h>
21#include <glib/gi18n.h>
22#include <gdk/gdkkeysyms.h>
23#include <gtk/gtkversion.h>
24#include <gtk/gtkfilechooserdialog.h>
25#include <gtk/gtkexpander.h>
26#include <gtk/gtkcombobox.h>
27#include <gtk/gtkstock.h>
28
29#ifdef G_OS_WIN32
30# define COBJMACROS
31# include <windows.h>
32# include <objbase.h>
33# include <objidl.h>
34# include <shlobj.h>
35#endif
36
37#include "main.h"
38#include "filesel.h"
39#include "manage_window.h"
40#include "alertpanel.h"
41#include "utils.h"
42#include "prefs_common.h"
43#include "inc.h"
44
45
46static GSList *filesel_select_file_full (const gchar *title,
47 const gchar *file,
48 GtkFileChooserAction action,
49 gboolean multiple,
50 FileselFileType *types,
51 gint default_type,
52 gint *selected_type);
53
54static GtkWidget *filesel_create (const gchar *title,
55 GtkFileChooserAction action);
56
57static void filesel_save_expander_set_expanded (GtkWidget *dialog,
58 gboolean expanded);
59static gboolean filesel_save_expander_get_expanded (GtkWidget *dialog);
60
61static gchar *filesel_get_filename_with_ext (const gchar *filename,
62 const gchar *ext);
63
64static void filesel_combo_changed_cb (GtkComboBox *combo_box,
65 gpointer data);
66
67#if GTK_CHECK_VERSION(2, 8, 0)
68static GtkFileChooserConfirmation filesel_confirm_overwrite_cb
69 (GtkFileChooser *chooser,
70 gpointer data);
71#endif
72#ifdef G_OS_WIN32
73static gboolean is_ext_lnk (const gchar *filename);
74static gchar *filesel_get_link (const gchar *link_file);
75static GSList *filesel_resolve_link (GtkFileChooser *chooser,
76 GSList *list,
77 gboolean *dir_selected);
78#endif
79
80
81gchar *filesel_select_file(const gchar *title, const gchar *file,
82 GtkFileChooserAction action)
83{
84 GSList *list;
85 gchar *selected = NULL;
86
87 list = filesel_select_file_full(title, file, action, FALSE, NULL, 0, NULL);
88 if (list) {
89 selected = (gchar *)list->data;
90 slist_free_strings(list->next);
91 }
92 g_slist_free(list);
93
94 return selected;
95}
96
97GSList *filesel_select_files(const gchar *title, const gchar *file,
98 GtkFileChooserAction action)
99{
100 return filesel_select_file_full(title, file, action, TRUE, NULL, 0, NULL);
101}
102
103static void filesel_change_dir_for_action(GtkFileChooserAction action)
104{
105 const gchar *cwd = NULL;
106
107 switch (action) {
108 case GTK_FILE_CHOOSER_ACTION_OPEN:
109 if (prefs_common.prev_open_dir &&
110 is_dir_exist(prefs_common.prev_open_dir))
111 cwd = prefs_common.prev_open_dir;
112 else {
113 g_free(prefs_common.prev_open_dir);
114 prefs_common.prev_open_dir = NULL;
115 }
116 break;
117 case GTK_FILE_CHOOSER_ACTION_SAVE:
118 if (prefs_common.prev_save_dir &&
119 is_dir_exist(prefs_common.prev_save_dir))
120 cwd = prefs_common.prev_save_dir;
121 else {
122 g_free(prefs_common.prev_save_dir);
123 prefs_common.prev_save_dir = NULL;
124 }
125 break;
126 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
127 if (prefs_common.prev_folder_dir &&
128 is_dir_exist(prefs_common.prev_folder_dir))
129 cwd = prefs_common.prev_folder_dir;
130 else {
131 g_free(prefs_common.prev_folder_dir);
132 prefs_common.prev_folder_dir = NULL;
133 }
134 break;
135 default:
136 break;
137 }
138
139 if (cwd)
140 change_dir(cwd);
141 else
142 change_dir(get_document_dir());
143}
144
145static void filesel_save_dir_for_action(GtkFileChooserAction action,
146 const gchar *cwd)
147{
148 switch (action) {
149 case GTK_FILE_CHOOSER_ACTION_OPEN:
150 g_free(prefs_common.prev_open_dir);
151 prefs_common.prev_open_dir = g_strdup(cwd);
152 break;
153 case GTK_FILE_CHOOSER_ACTION_SAVE:
154 g_free(prefs_common.prev_save_dir);
155 prefs_common.prev_save_dir = g_strdup(cwd);
156 break;
157 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
158 g_free(prefs_common.prev_folder_dir);
159 prefs_common.prev_folder_dir = g_strdup(cwd);
160 break;
161 default:
162 break;
163 }
164}
165
166static GSList *filesel_select_file_full(const gchar *title, const gchar *file,
167 GtkFileChooserAction action,
168 gboolean multiple,
169 FileselFileType *types,
170 gint default_type, gint *selected_type)
171{
172 gchar *cwd;
173 GtkWidget *dialog;
174 gchar *prev_dir;
175 static gboolean save_expander_expanded = FALSE;
176 GSList *list = NULL;
177 GtkWidget *combo = NULL;
178
179 prev_dir = g_get_current_dir();
180
181 filesel_change_dir_for_action(action);
182
183 dialog = filesel_create(title, action);
184
185 manage_window_set_transient(GTK_WINDOW(dialog));
186
187 cwd = g_get_current_dir();
188 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), cwd);
189 g_free(cwd);
190 cwd = NULL;
191
192 if (file)
193 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
194 file);
195
196 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),
197 multiple);
198
199 if (action == GTK_FILE_CHOOSER_ACTION_SAVE && save_expander_expanded) {
200 filesel_save_expander_set_expanded
201 (dialog, save_expander_expanded);
202 }
203
204#if GTK_CHECK_VERSION(2, 8, 0)
205 if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
206 gtk_file_chooser_set_do_overwrite_confirmation
207 (GTK_FILE_CHOOSER(dialog), TRUE);
208 g_signal_connect(GTK_FILE_CHOOSER(dialog), "confirm-overwrite",
209 G_CALLBACK(filesel_confirm_overwrite_cb),
210 NULL);
211 }
212#endif
213
214 /* create types combo box */
215 if (types) {
216 GtkWidget *hbox;
217 GtkWidget *label;
218 gint i;
219
220 hbox = gtk_hbox_new(FALSE, 12);
221 label = gtk_label_new(_("File type:"));
222 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
223
224 combo = gtk_combo_box_new_text();
225 for (i = 0; types[i].type != NULL; i++) {
226 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), types[i].type);
227 }
228 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
229
230 gtk_widget_show_all(hbox);
231 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), hbox);
232 if (default_type < 0 || default_type >= i)
233 default_type = 0;
234 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), default_type);
235 g_object_set_data(G_OBJECT(combo), "types", types);
236 g_signal_connect(GTK_COMBO_BOX(combo), "changed",
237 G_CALLBACK(filesel_combo_changed_cb), dialog);
238
239 if (file) {
240 gchar *newfile;
241
242 newfile = filesel_get_filename_with_ext
243 (file, types[default_type].ext);
244 gtk_file_chooser_set_current_name
245 (GTK_FILE_CHOOSER(dialog), newfile);
246 g_free(newfile);
247 }
248 }
249
250 gtk_widget_show(dialog);
251
252 change_dir(prev_dir);
253 g_free(prev_dir);
254
255 inc_lock();
256
257#ifdef G_OS_WIN32
258again:
259#endif
260 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
261 list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
262#ifdef G_OS_WIN32
263 if (list) {
264 /* resolve Windows shortcut */
265 gboolean dir_selected = FALSE;
266 list = filesel_resolve_link(GTK_FILE_CHOOSER(dialog),
267 list, &dir_selected);
268 if (!list) {
269 if (!dir_selected)
270 alertpanel_error(_("The link target not found."));
271 goto again;
272 }
273 }
274#endif
275 if (list) {
276 cwd = gtk_file_chooser_get_current_folder
277 (GTK_FILE_CHOOSER(dialog));
278 if (cwd) {
279 filesel_save_dir_for_action(action, cwd);
280 g_free(cwd);
281 }
282 }
283 }
284
285 inc_unlock();
286
287 if (combo && selected_type) {
288 *selected_type = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
289 }
290
291 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
292 save_expander_expanded =
293 filesel_save_expander_get_expanded(dialog);
294
295 manage_window_focus_out(dialog, NULL, NULL);
296 gtk_widget_destroy(dialog);
297
298 return list;
299}
300
301gchar *filesel_save_as(const gchar *file)
302{
303 gchar *filename;
304
305 filename = filesel_select_file(_("Save as"), file,
306 GTK_FILE_CHOOSER_ACTION_SAVE);
307
308#if !GTK_CHECK_VERSION(2, 8, 0)
309 if (filename && is_file_exist(filename)) {
310 AlertValue aval;
311
312 aval = alertpanel(_("Overwrite existing file"),
313 _("The file already exists. Do you want to replace it?"),
314 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
315 if (G_ALERTDEFAULT != aval) {
316 g_free(filename);
317 filename = NULL;
318 }
319 }
320#endif
321
322 return filename;
323}
324
325gchar *filesel_save_as_type(const gchar *file, FileselFileType *types,
326 gint default_type, gint *selected_type)
327{
328 GSList *list;
329 gchar *filename = NULL;
330
331 list = filesel_select_file_full(_("Save as"), file,
332 GTK_FILE_CHOOSER_ACTION_SAVE, FALSE,
333 types,
334 default_type, selected_type);
335 if (list) {
336 filename = (gchar *)list->data;
337 slist_free_strings(list->next);
338 }
339 g_slist_free(list);
340
341#if !GTK_CHECK_VERSION(2, 8, 0)
342 if (filename && is_file_exist(filename)) {
343 AlertValue aval;
344
345 aval = alertpanel(_("Overwrite existing file"),
346 _("The file already exists. Do you want to replace it?"),
347 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
348 if (G_ALERTDEFAULT != aval) {
349 g_free(filename);
350 filename = NULL;
351 }
352 }
353#endif
354
355 return filename;
356}
357
358gchar *filesel_select_dir(const gchar *dir)
359{
360 GSList *list;
361 gchar *selected = NULL;
362
363 list = filesel_select_file_full(_("Select folder"), dir,
364 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
365 FALSE, NULL, 0, NULL);
366 if (list) {
367 selected = (gchar *)list->data;
368 slist_free_strings(list->next);
369 }
370 g_slist_free(list);
371
372 return selected;
373}
374
375static GtkWidget *filesel_create(const gchar *title,
376 GtkFileChooserAction action)
377{
378 GtkWidget *dialog;
379
380 if (prefs_common.comply_gnome_hig)
381 dialog = gtk_file_chooser_dialog_new
382 (title, NULL, action,
383 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
384 (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
385 action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
386 ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
387 NULL);
388 else
389 dialog = gtk_file_chooser_dialog_new
390 (title, NULL, action,
391 (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
392 action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
393 ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
394 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
395 NULL);
396 gtk_window_set_position(GTK_WINDOW(dialog),
397 GTK_WIN_POS_CENTER_ON_PARENT);
398 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
399 gtk_window_set_wmclass
400 (GTK_WINDOW(dialog), "file_selection", "Sylpheed");
401 gtk_dialog_set_default_response
402 (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
403
404 MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
405
406 return dialog;
407}
408
409static void container_foreach_cb(GtkWidget *widget, gpointer data)
410{
411 GtkWidget **expander = (GtkWidget **)data;
412
413 if (*expander == NULL) {
414 if (GTK_IS_EXPANDER(widget))
415 *expander = widget;
416 else if (GTK_IS_CONTAINER(widget))
417 gtk_container_foreach(GTK_CONTAINER(widget),
418 container_foreach_cb, data);
419 }
420}
421
422static void filesel_save_expander_set_expanded(GtkWidget *dialog,
423 gboolean expanded)
424{
425 GtkWidget *expander = NULL;
426
427 gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
428 &expander);
429 if (expander)
430 gtk_expander_set_expanded(GTK_EXPANDER(expander), expanded);
431}
432
433static gboolean filesel_save_expander_get_expanded(GtkWidget *dialog)
434{
435 GtkWidget *expander = NULL;
436
437 gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
438 &expander);
439 if (expander)
440 return gtk_expander_get_expanded(GTK_EXPANDER(expander));
441 else
442 return FALSE;
443}
444
445static gchar *filesel_get_filename_with_ext(const gchar *filename, const gchar *ext)
446{
447 gchar *base;
448 gchar *new_filename;
449 gchar *p;
450
451 base = g_path_get_basename(filename);
452 p = strrchr(base, '.');
453 if (p)
454 *p = '\0';
455 new_filename = g_strconcat(base, ".", ext, NULL);
456 debug_print("new_filename: %s\n", new_filename);
457 g_free(base);
458
459 return new_filename;
460}
461
462static void filesel_combo_changed_cb(GtkComboBox *combo_box, gpointer data)
463{
464 GtkFileChooser *chooser = data;
465 gint active;
466 gchar *filename;
467 gchar *new_filename;
468 FileselFileType *types;
469
470 active = gtk_combo_box_get_active(combo_box);
471 filename = gtk_file_chooser_get_filename(chooser);
472 if (!filename)
473 return;
474 types = g_object_get_data(G_OBJECT(combo_box), "types");
475 debug_print("active: %d filename: %s\n", active, filename);
476 debug_print("type ext: %s\n", types[active].ext);
477 new_filename = filesel_get_filename_with_ext(filename, types[active].ext);
478 gtk_file_chooser_set_current_name(chooser, new_filename);
479 g_free(new_filename);
480 g_free(filename);
481}
482
483#if GTK_CHECK_VERSION(2, 8, 0)
484static GtkFileChooserConfirmation filesel_confirm_overwrite_cb
485 (GtkFileChooser *chooser,
486 gpointer data)
487{
488 gchar *filename;
489 GtkFileChooserConfirmation ret =
490 GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
491
492 filename = gtk_file_chooser_get_filename(chooser);
493
494 if (filename && is_file_exist(filename)) {
495 AlertValue aval;
496
497#ifdef G_OS_WIN32
498 gchar *target = NULL;
499
500 if (is_ext_lnk(filename) &&
501 (target = filesel_get_link(filename)) != NULL &&
502 is_dir_exist(target)) {
503 gtk_file_chooser_set_current_folder(chooser, target);
504 g_free(target);
505 g_free(filename);
506 return GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
507 }
508 g_free(target);
509#endif
510
511 aval = alertpanel(_("Overwrite existing file"),
512 _("The file already exists. Do you want to replace it?"),
513 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
514 if (G_ALERTDEFAULT == aval)
515 ret = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
516 else
517 ret = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
518 }
519
520 g_free(filename);
521
522 return ret;
523}
524#endif
525
526#ifdef G_OS_WIN32
527static gboolean is_ext_lnk(const gchar *filename)
528{
529 const gchar *ext;
530
531 if (filename && (ext = strrchr(filename, '.')) &&
532 g_ascii_strcasecmp(ext, ".lnk") == 0)
533 return TRUE;
534
535 return FALSE;
536}
537
538static gchar *filesel_get_link(const gchar *link_file)
539{
540 WIN32_FIND_DATAW wfd;
541 IShellLinkW *psl;
542 IPersistFile *ppf;
543 HRESULT hr;
544 wchar_t *wlink_file;
545 wchar_t wtarget[MAX_PATH];
546 gchar *target = NULL;
547
548 wtarget[0] = 0L;
549
550 debug_print("link_file: %s\n", link_file);
551
552 CoInitialize(NULL);
553 if (S_OK == CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (void **)&psl)) {
554 if (S_OK == IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (void **)&ppf)) {
555 wlink_file = g_utf8_to_utf16(link_file, -1, NULL, NULL, NULL);
556 if (S_OK == IPersistFile_Load(ppf, wlink_file, STGM_READ)) {
557 if (S_OK == IShellLinkW_GetPath(psl, wtarget, MAX_PATH, &wfd, SLGP_UNCPRIORITY)) {
558 target = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
559 }
560 }
561 IPersistFile_Release(ppf);
562 g_free(wlink_file);
563 }
564 IShellLinkW_Release(psl);
565 }
566 CoUninitialize();
567
568 if (target)
569 debug_print("target: %s\n", target);
570 else
571 debug_print("target not found\n");
572
573 return target;
574}
575
576static GSList *filesel_resolve_link(GtkFileChooser *chooser, GSList *list, gboolean *dir_selected)
577{
578 GSList *cur;
579 GSList *new_list = NULL;
580 gchar *target;
581
582 for (cur = list; cur != NULL; cur = cur->next) {
583 gchar *selected = (gchar *)cur->data;
584
585 if (is_ext_lnk(selected)) {
586 target = filesel_get_link(selected);
587
588 if (is_dir_exist(target)) {
589 gtk_file_chooser_set_current_folder(chooser, target);
590 g_free(target);
591 slist_free_strings(new_list);
592 g_slist_free(new_list);
593 new_list = NULL;
594 *dir_selected = TRUE;
595 break;
596 } else if (is_file_exist(target)) {
597 new_list = g_slist_append(new_list, target);
598 } else {
599 g_free(target);
600 }
601 } else {
602 new_list = g_slist_append(new_list, g_strdup(selected));
603 }
604 }
605
606 slist_free_strings(list);
607 g_slist_free(list);
608
609 return new_list;
610}
611#endif