Statistics
| Revision:

root / src / filesel.c @ 3259

History | View | Annotate | Download (15.2 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2013 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

    
46
static 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

    
54
static GtkWidget *filesel_create        (const gchar                *title,
55
                                         GtkFileChooserAction         action);
56

    
57
static void filesel_save_expander_set_expanded           (GtkWidget        *dialog,
58
                                                    gboolean         expanded);
59
static gboolean filesel_save_expander_get_expanded (GtkWidget        *dialog);
60

    
61
static gchar *filesel_get_filename_with_ext        (const gchar        *filename,
62
                                                 const gchar        *ext);
63

    
64
static void filesel_combo_changed_cb        (GtkComboBox                *combo_box,
65
                                         gpointer                 data);
66

    
67
#if GTK_CHECK_VERSION(2, 8, 0)
68
static GtkFileChooserConfirmation filesel_confirm_overwrite_cb
69
                                        (GtkFileChooser                *chooser,
70
                                         gpointer                 data);
71
#endif
72
#ifdef G_OS_WIN32
73
static gboolean is_ext_lnk                (const gchar                *filename);
74
static gchar *filesel_get_link                (const gchar                *link_file);
75
static GSList *filesel_resolve_link        (GtkFileChooser                *chooser,
76
                                         GSList                        *list,
77
                                         gboolean                *dir_selected);
78
#endif
79

    
80

    
81
gchar *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

    
97
GSList *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

    
103
static 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

    
145
static 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

    
166
static 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
        if (file)
188
                gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
189
                                                  file);
190

    
191
        gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),
192
                                             multiple);
193

    
194
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE && save_expander_expanded) {
195
                filesel_save_expander_set_expanded
196
                        (dialog, save_expander_expanded);
197
        }
198

    
199
#if GTK_CHECK_VERSION(2, 8, 0)
200
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
201
                gtk_file_chooser_set_do_overwrite_confirmation
202
                        (GTK_FILE_CHOOSER(dialog), TRUE);
203
                g_signal_connect(GTK_FILE_CHOOSER(dialog), "confirm-overwrite",
204
                                 G_CALLBACK(filesel_confirm_overwrite_cb),
205
                                 NULL);
206
        }
207
#endif
208

    
209
        /* create types combo box */
210
        if (types) {
211
                GtkWidget *hbox;
212
                GtkWidget *label;
213
                gint i;
214

    
215
                hbox = gtk_hbox_new(FALSE, 12);
216
                label = gtk_label_new(_("File type:"));
217
                gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
218

    
219
                combo = gtk_combo_box_new_text();
220
                for (i = 0; types[i].type != NULL; i++) {
221
                        gtk_combo_box_append_text(GTK_COMBO_BOX(combo), types[i].type);
222
                }
223
                gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
224

    
225
                gtk_widget_show_all(hbox);
226
                gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), hbox);
227
                if (default_type < 0 || default_type >= i)
228
                        default_type = 0;
229
                gtk_combo_box_set_active(GTK_COMBO_BOX(combo), default_type);
230
                g_object_set_data(G_OBJECT(combo), "types", types);
231
                g_signal_connect(GTK_COMBO_BOX(combo), "changed",
232
                                 G_CALLBACK(filesel_combo_changed_cb), dialog);
233

    
234
                if (file) {
235
                        gchar *newfile;
236

    
237
                        newfile = filesel_get_filename_with_ext
238
                                (file, types[default_type].ext);
239
                        gtk_file_chooser_set_current_name
240
                                (GTK_FILE_CHOOSER(dialog), newfile);
241
                        g_free(newfile);
242
                }
243
        }
244

    
245
        gtk_widget_show(dialog);
246

    
247
        change_dir(prev_dir);
248
        g_free(prev_dir);
249

    
250
        inc_lock();
251

    
252
#ifdef G_OS_WIN32
253
again:
254
#endif
255
        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
256
                list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
257
#ifdef G_OS_WIN32
258
                if (list) {
259
                        /* resolve Windows shortcut */
260
                        gboolean dir_selected = FALSE;
261
                        list = filesel_resolve_link(GTK_FILE_CHOOSER(dialog),
262
                                                    list, &dir_selected);
263
                        if (!list) {
264
                                if (!dir_selected)
265
                                        alertpanel_error(_("The link target not found."));
266
                                goto again;
267
                        }
268
                }
269
#endif
270
                if (list) {
271
                        cwd = gtk_file_chooser_get_current_folder
272
                                (GTK_FILE_CHOOSER(dialog));
273
                        if (cwd) {
274
                                filesel_save_dir_for_action(action, cwd);
275
                                g_free(cwd);
276
                        }
277
                }
278
        }
279

    
280
        inc_unlock();
281

    
282
        if (combo && selected_type) {
283
                *selected_type = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
284
        }
285

    
286
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
287
                save_expander_expanded =
288
                        filesel_save_expander_get_expanded(dialog);
289

    
290
        manage_window_focus_out(dialog, NULL, NULL);
291
        gtk_widget_destroy(dialog);
292

    
293
        return list;
294
}
295

    
296
gchar *filesel_save_as(const gchar *file)
297
{
298
        gchar *filename;
299

    
300
        filename = filesel_select_file(_("Save as"), file,
301
                                       GTK_FILE_CHOOSER_ACTION_SAVE);
302

    
303
#if !GTK_CHECK_VERSION(2, 8, 0)
304
        if (filename && is_file_exist(filename)) {
305
                AlertValue aval;
306

    
307
                aval = alertpanel(_("Overwrite existing file"),
308
                                  _("The file already exists. Do you want to replace it?"),
309
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
310
                if (G_ALERTDEFAULT != aval) {
311
                        g_free(filename);
312
                        filename = NULL;
313
                }
314
        }
315
#endif
316

    
317
        return filename;
318
}
319

    
320
gchar *filesel_save_as_type(const gchar *file, FileselFileType *types,
321
                            gint default_type, gint *selected_type)
322
{
323
        GSList *list;
324
        gchar *filename = NULL;
325

    
326
        list = filesel_select_file_full(_("Save as"), file,
327
                                        GTK_FILE_CHOOSER_ACTION_SAVE, FALSE,
328
                                        types,
329
                                        default_type, selected_type);
330
        if (list) {
331
                filename = (gchar *)list->data;
332
                slist_free_strings(list->next);
333
        }
334
        g_slist_free(list);
335

    
336
#if !GTK_CHECK_VERSION(2, 8, 0)
337
        if (filename && is_file_exist(filename)) {
338
                AlertValue aval;
339

    
340
                aval = alertpanel(_("Overwrite existing file"),
341
                                  _("The file already exists. Do you want to replace it?"),
342
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
343
                if (G_ALERTDEFAULT != aval) {
344
                        g_free(filename);
345
                        filename = NULL;
346
                }
347
        }
348
#endif
349

    
350
        return filename;
351
}
352

    
353
gchar *filesel_select_dir(const gchar *dir)
354
{
355
        GSList *list;
356
        gchar *selected = NULL;
357

    
358
        list = filesel_select_file_full(_("Select folder"), dir,
359
                                        GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
360
                                        FALSE, NULL, 0, NULL);
361
        if (list) {
362
                selected = (gchar *)list->data;
363
                slist_free_strings(list->next);
364
        }
365
        g_slist_free(list);
366

    
367
        return selected;
368
}
369

    
370
static GtkWidget *filesel_create(const gchar *title,
371
                                 GtkFileChooserAction action)
372
{
373
        GtkWidget *dialog;
374

    
375
        if (prefs_common.comply_gnome_hig)
376
                dialog = gtk_file_chooser_dialog_new
377
                        (title, NULL, action,
378
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
379
                         (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
380
                          action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
381
                         ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
382
                         NULL);
383
        else
384
                dialog = gtk_file_chooser_dialog_new
385
                        (title, NULL, action,
386
                         (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
387
                          action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
388
                         ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
389
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
390
                         NULL);
391
        gtk_window_set_position(GTK_WINDOW(dialog),
392
                                GTK_WIN_POS_CENTER_ON_PARENT);
393
        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
394
        gtk_window_set_wmclass
395
                (GTK_WINDOW(dialog), "file_selection", "Sylpheed");
396
        gtk_dialog_set_default_response
397
                (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
398

    
399
        MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
400

    
401
        return dialog;
402
}
403

    
404
static void container_foreach_cb(GtkWidget *widget, gpointer data)
405
{
406
        GtkWidget **expander = (GtkWidget **)data;
407

    
408
        if (*expander == NULL) {
409
                if (GTK_IS_EXPANDER(widget))
410
                        *expander = widget;
411
                else if (GTK_IS_CONTAINER(widget))
412
                        gtk_container_foreach(GTK_CONTAINER(widget),
413
                                              container_foreach_cb, data);
414
        }
415
}
416

    
417
static void filesel_save_expander_set_expanded(GtkWidget *dialog,
418
                                               gboolean expanded)
419
{
420
        GtkWidget *expander = NULL;
421

    
422
        gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
423
                              &expander);
424
        if (expander)
425
                gtk_expander_set_expanded(GTK_EXPANDER(expander), expanded);
426
}
427

    
428
static gboolean filesel_save_expander_get_expanded(GtkWidget *dialog)
429
{
430
        GtkWidget *expander = NULL;
431

    
432
        gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
433
                              &expander);
434
        if (expander)
435
                return gtk_expander_get_expanded(GTK_EXPANDER(expander));
436
        else
437
                return FALSE;
438
}
439

    
440
static gchar *filesel_get_filename_with_ext(const gchar *filename, const gchar *ext)
441
{
442
        gchar *base;
443
        gchar *new_filename;
444
        gchar *p;
445

    
446
        base = g_path_get_basename(filename);
447
        p = strrchr(base, '.');
448
        if (p)
449
                *p = '\0';
450
        new_filename = g_strconcat(base, ".", ext, NULL);
451
        debug_print("new_filename: %s\n", new_filename);
452
        g_free(base);
453

    
454
        return new_filename;
455
}
456

    
457
static void filesel_combo_changed_cb(GtkComboBox *combo_box, gpointer data)
458
{
459
        GtkFileChooser *chooser = data;
460
        gint active;
461
        gchar *filename;
462
        gchar *new_filename;
463
        FileselFileType *types;
464

    
465
        active = gtk_combo_box_get_active(combo_box);
466
        filename = gtk_file_chooser_get_filename(chooser);
467
        if (!filename)
468
                return;
469
        types = g_object_get_data(G_OBJECT(combo_box), "types");
470
        debug_print("active: %d filename: %s\n", active, filename);
471
        debug_print("type ext: %s\n", types[active].ext);
472
        new_filename = filesel_get_filename_with_ext(filename, types[active].ext);
473
        gtk_file_chooser_set_current_name(chooser, new_filename);
474
        g_free(new_filename);
475
        g_free(filename);
476
}
477

    
478
#if GTK_CHECK_VERSION(2, 8, 0)
479
static GtkFileChooserConfirmation filesel_confirm_overwrite_cb
480
                                        (GtkFileChooser                *chooser,
481
                                         gpointer                 data)
482
{
483
        gchar *filename;
484
        GtkFileChooserConfirmation ret =
485
                GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
486

    
487
        filename = gtk_file_chooser_get_filename(chooser);
488

    
489
        if (filename && is_file_exist(filename)) {
490
                AlertValue aval;
491

    
492
#ifdef G_OS_WIN32
493
                gchar *target = NULL;
494

    
495
                if (is_ext_lnk(filename) &&
496
                    (target = filesel_get_link(filename)) != NULL &&
497
                    is_dir_exist(target)) {
498
                        gtk_file_chooser_set_current_folder(chooser, target);
499
                            g_free(target);
500
                            g_free(filename);
501
                            return GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
502
                }
503
                g_free(target);
504
#endif
505

    
506
                aval = alertpanel(_("Overwrite existing file"),
507
                                  _("The file already exists. Do you want to replace it?"),
508
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
509
                if (G_ALERTDEFAULT == aval)
510
                        ret = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
511
                else
512
                        ret = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
513
        }
514

    
515
        g_free(filename);
516

    
517
        return ret;
518
}
519
#endif
520

    
521
#ifdef G_OS_WIN32
522
static gboolean is_ext_lnk(const gchar *filename)
523
{
524
        const gchar *ext;
525

    
526
        if (filename && (ext = strrchr(filename, '.')) &&
527
            g_ascii_strcasecmp(ext, ".lnk") == 0)
528
                return TRUE;
529

    
530
        return FALSE;
531
}
532

    
533
static gchar *filesel_get_link(const gchar *link_file)
534
{
535
        WIN32_FIND_DATAW wfd;
536
        IShellLinkW *psl;
537
        IPersistFile *ppf;
538
        HRESULT hr;
539
        wchar_t *wlink_file;
540
        wchar_t wtarget[MAX_PATH];
541
        gchar *target = NULL;
542

    
543
        wtarget[0] = 0L;
544

    
545
        debug_print("link_file: %s\n", link_file);
546

    
547
        CoInitialize(NULL);
548
        if (S_OK == CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (void **)&psl)) {
549
                if (S_OK == IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (void **)&ppf)) {
550
                        wlink_file = g_utf8_to_utf16(link_file, -1, NULL, NULL, NULL);
551
                        if (S_OK == IPersistFile_Load(ppf, wlink_file, STGM_READ)) {
552
                                if (S_OK == IShellLinkW_GetPath(psl, wtarget, MAX_PATH, &wfd, SLGP_UNCPRIORITY)) {
553
                                        target = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
554
                                }
555
                        }
556
                        IPersistFile_Release(ppf);
557
                        g_free(wlink_file);
558
                }
559
                IShellLinkW_Release(psl);
560
        }
561
        CoUninitialize();
562

    
563
        if (target)
564
                debug_print("target: %s\n", target);
565
        else
566
                debug_print("target not found\n");
567

    
568
        return target;
569
}
570

    
571
static GSList *filesel_resolve_link(GtkFileChooser *chooser, GSList *list, gboolean *dir_selected)
572
{
573
        GSList *cur;
574
        GSList *new_list = NULL;
575
        gchar *target;
576

    
577
        for (cur = list; cur != NULL; cur = cur->next) {
578
                gchar *selected = (gchar *)cur->data;
579

    
580
                if (is_ext_lnk(selected)) {
581
                        target = filesel_get_link(selected);
582

    
583
                            if (is_dir_exist(target)) {
584
                                gtk_file_chooser_set_current_folder(chooser, target);
585
                                g_free(target);
586
                                slist_free_strings(new_list);
587
                                g_slist_free(new_list);
588
                                    new_list = NULL;
589
                                *dir_selected = TRUE;
590
                                    break;
591
                        } else if (is_file_exist(target)) {
592
                                new_list = g_slist_append(new_list, target);
593
                        } else {
594
                                    g_free(target);
595
                        }
596
                } else {
597
                        new_list = g_slist_append(new_list, g_strdup(selected));
598
                }
599
        }
600

    
601
        slist_free_strings(list);
602
        g_slist_free(list);
603

    
604
        return new_list;
605
}
606
#endif