Statistics
| Revision:

root / src / filesel.c @ 3258

History | View | Annotate | Download (14.6 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 void filesel_combo_changed_cb        (GtkComboBox                *combo_box,
62
                                         gpointer                 data);
63

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

    
77

    
78
gchar *filesel_select_file(const gchar *title, const gchar *file,
79
                           GtkFileChooserAction action)
80
{
81
        GSList *list;
82
        gchar *selected = NULL;
83

    
84
        list = filesel_select_file_full(title, file, action, FALSE, NULL, 0, NULL);
85
        if (list) {
86
                selected = (gchar *)list->data;
87
                slist_free_strings(list->next);
88
        }
89
        g_slist_free(list);
90

    
91
        return selected;
92
}
93

    
94
GSList *filesel_select_files(const gchar *title, const gchar *file,
95
                             GtkFileChooserAction action)
96
{
97
        return filesel_select_file_full(title, file, action, TRUE, NULL, 0, NULL);
98
}
99

    
100
static void filesel_change_dir_for_action(GtkFileChooserAction action)
101
{
102
        const gchar *cwd = NULL;
103

    
104
        switch (action) {
105
        case GTK_FILE_CHOOSER_ACTION_OPEN:
106
                if (prefs_common.prev_open_dir &&
107
                    is_dir_exist(prefs_common.prev_open_dir))
108
                        cwd = prefs_common.prev_open_dir;
109
                else {
110
                        g_free(prefs_common.prev_open_dir);
111
                        prefs_common.prev_open_dir = NULL;
112
                }
113
                break;
114
        case GTK_FILE_CHOOSER_ACTION_SAVE:
115
                if (prefs_common.prev_save_dir &&
116
                    is_dir_exist(prefs_common.prev_save_dir))
117
                        cwd = prefs_common.prev_save_dir;
118
                else {
119
                        g_free(prefs_common.prev_save_dir);
120
                        prefs_common.prev_save_dir = NULL;
121
                }
122
                break;
123
        case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
124
                if (prefs_common.prev_folder_dir &&
125
                    is_dir_exist(prefs_common.prev_folder_dir))
126
                        cwd = prefs_common.prev_folder_dir;
127
                else {
128
                        g_free(prefs_common.prev_folder_dir);
129
                        prefs_common.prev_folder_dir = NULL;
130
                }
131
                break;
132
        default:
133
                break;
134
        }
135

    
136
        if (cwd)
137
                change_dir(cwd);
138
        else
139
                change_dir(get_document_dir());
140
}
141

    
142
static void filesel_save_dir_for_action(GtkFileChooserAction action,
143
                                        const gchar *cwd)
144
{
145
        switch (action) {
146
        case GTK_FILE_CHOOSER_ACTION_OPEN:
147
                g_free(prefs_common.prev_open_dir);
148
                prefs_common.prev_open_dir = g_strdup(cwd);
149
                break;
150
        case GTK_FILE_CHOOSER_ACTION_SAVE:
151
                g_free(prefs_common.prev_save_dir);
152
                prefs_common.prev_save_dir = g_strdup(cwd);
153
                break;
154
        case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
155
                g_free(prefs_common.prev_folder_dir);
156
                prefs_common.prev_folder_dir = g_strdup(cwd);
157
                break;
158
        default:
159
                break;
160
        }
161
}
162

    
163
static GSList *filesel_select_file_full(const gchar *title, const gchar *file,
164
                                        GtkFileChooserAction action,
165
                                        gboolean multiple,
166
                                        FileselFileType *types,
167
                                        gint default_type, gint *selected_type)
168
{
169
        gchar *cwd;
170
        GtkWidget *dialog;
171
        gchar *prev_dir;
172
        static gboolean save_expander_expanded = FALSE;
173
        GSList *list = NULL;
174
        GtkWidget *combo = NULL;
175

    
176
        prev_dir = g_get_current_dir();
177

    
178
        filesel_change_dir_for_action(action);
179

    
180
        dialog = filesel_create(title, action);
181

    
182
        manage_window_set_transient(GTK_WINDOW(dialog));
183

    
184
        if (file)
185
                gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
186
                                                  file);
187

    
188
        gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),
189
                                             multiple);
190

    
191
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE && save_expander_expanded) {
192
                filesel_save_expander_set_expanded
193
                        (dialog, save_expander_expanded);
194
        }
195

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

    
206
        /* create types combo box */
207
        if (types) {
208
                GtkWidget *hbox;
209
                GtkWidget *label;
210
                gint i;
211

    
212
                hbox = gtk_hbox_new(FALSE, 12);
213
                label = gtk_label_new(_("File type:"));
214
                gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
215

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

    
222
                gtk_widget_show_all(hbox);
223
                gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), hbox);
224
                gtk_combo_box_set_active(GTK_COMBO_BOX(combo), default_type);
225

    
226
                g_object_set_data(G_OBJECT(combo), "types", types);
227
                g_signal_connect(GTK_COMBO_BOX(combo), "changed",
228
                                 G_CALLBACK(filesel_combo_changed_cb), dialog);
229
        }
230

    
231
        gtk_widget_show(dialog);
232

    
233
        change_dir(prev_dir);
234
        g_free(prev_dir);
235

    
236
        inc_lock();
237

    
238
#ifdef G_OS_WIN32
239
again:
240
#endif
241
        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
242
                list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
243
#ifdef G_OS_WIN32
244
                if (list) {
245
                        /* resolve Windows shortcut */
246
                        gboolean dir_selected = FALSE;
247
                        list = filesel_resolve_link(GTK_FILE_CHOOSER(dialog),
248
                                                    list, &dir_selected);
249
                        if (!list) {
250
                                if (!dir_selected)
251
                                        alertpanel_error(_("The link target not found."));
252
                                goto again;
253
                        }
254
                }
255
#endif
256
                if (list) {
257
                        cwd = gtk_file_chooser_get_current_folder
258
                                (GTK_FILE_CHOOSER(dialog));
259
                        if (cwd) {
260
                                filesel_save_dir_for_action(action, cwd);
261
                                g_free(cwd);
262
                        }
263
                }
264
        }
265

    
266
        inc_unlock();
267

    
268
        if (combo && selected_type) {
269
                *selected_type = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
270
        }
271

    
272
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
273
                save_expander_expanded =
274
                        filesel_save_expander_get_expanded(dialog);
275

    
276
        manage_window_focus_out(dialog, NULL, NULL);
277
        gtk_widget_destroy(dialog);
278

    
279
        return list;
280
}
281

    
282
gchar *filesel_save_as(const gchar *file)
283
{
284
        gchar *filename;
285

    
286
        filename = filesel_select_file(_("Save as"), file,
287
                                       GTK_FILE_CHOOSER_ACTION_SAVE);
288

    
289
#if !GTK_CHECK_VERSION(2, 8, 0)
290
        if (filename && is_file_exist(filename)) {
291
                AlertValue aval;
292

    
293
                aval = alertpanel(_("Overwrite existing file"),
294
                                  _("The file already exists. Do you want to replace it?"),
295
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
296
                if (G_ALERTDEFAULT != aval) {
297
                        g_free(filename);
298
                        filename = NULL;
299
                }
300
        }
301
#endif
302

    
303
        return filename;
304
}
305

    
306
gchar *filesel_save_as_type(const gchar *file, FileselFileType *types,
307
                            gint default_type, gint *selected_type)
308
{
309
        GSList *list;
310
        gchar *filename = NULL;
311

    
312
        list = filesel_select_file_full(_("Save as"), file,
313
                                        GTK_FILE_CHOOSER_ACTION_SAVE, FALSE,
314
                                        types,
315
                                        default_type, selected_type);
316
        if (list) {
317
                filename = (gchar *)list->data;
318
                slist_free_strings(list->next);
319
        }
320
        g_slist_free(list);
321

    
322
#if !GTK_CHECK_VERSION(2, 8, 0)
323
        if (filename && is_file_exist(filename)) {
324
                AlertValue aval;
325

    
326
                aval = alertpanel(_("Overwrite existing file"),
327
                                  _("The file already exists. Do you want to replace it?"),
328
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
329
                if (G_ALERTDEFAULT != aval) {
330
                        g_free(filename);
331
                        filename = NULL;
332
                }
333
        }
334
#endif
335

    
336
        return filename;
337
}
338

    
339
gchar *filesel_select_dir(const gchar *dir)
340
{
341
        GSList *list;
342
        gchar *selected = NULL;
343

    
344
        list = filesel_select_file_full(_("Select folder"), dir,
345
                                        GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
346
                                        FALSE, NULL, 0, NULL);
347
        if (list) {
348
                selected = (gchar *)list->data;
349
                slist_free_strings(list->next);
350
        }
351
        g_slist_free(list);
352

    
353
        return selected;
354
}
355

    
356
static GtkWidget *filesel_create(const gchar *title,
357
                                 GtkFileChooserAction action)
358
{
359
        GtkWidget *dialog;
360

    
361
        if (prefs_common.comply_gnome_hig)
362
                dialog = gtk_file_chooser_dialog_new
363
                        (title, NULL, action,
364
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
365
                         (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
366
                          action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
367
                         ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
368
                         NULL);
369
        else
370
                dialog = gtk_file_chooser_dialog_new
371
                        (title, NULL, action,
372
                         (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
373
                          action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
374
                         ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
375
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
376
                         NULL);
377
        gtk_window_set_position(GTK_WINDOW(dialog),
378
                                GTK_WIN_POS_CENTER_ON_PARENT);
379
        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
380
        gtk_window_set_wmclass
381
                (GTK_WINDOW(dialog), "file_selection", "Sylpheed");
382
        gtk_dialog_set_default_response
383
                (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
384

    
385
        MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
386

    
387
        return dialog;
388
}
389

    
390
static void container_foreach_cb(GtkWidget *widget, gpointer data)
391
{
392
        GtkWidget **expander = (GtkWidget **)data;
393

    
394
        if (*expander == NULL) {
395
                if (GTK_IS_EXPANDER(widget))
396
                        *expander = widget;
397
                else if (GTK_IS_CONTAINER(widget))
398
                        gtk_container_foreach(GTK_CONTAINER(widget),
399
                                              container_foreach_cb, data);
400
        }
401
}
402

    
403
static void filesel_save_expander_set_expanded(GtkWidget *dialog,
404
                                               gboolean expanded)
405
{
406
        GtkWidget *expander = NULL;
407

    
408
        gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
409
                              &expander);
410
        if (expander)
411
                gtk_expander_set_expanded(GTK_EXPANDER(expander), expanded);
412
}
413

    
414
static gboolean filesel_save_expander_get_expanded(GtkWidget *dialog)
415
{
416
        GtkWidget *expander = NULL;
417

    
418
        gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
419
                              &expander);
420
        if (expander)
421
                return gtk_expander_get_expanded(GTK_EXPANDER(expander));
422
        else
423
                return FALSE;
424
}
425

    
426
static void filesel_combo_changed_cb(GtkComboBox *combo_box, gpointer data)
427
{
428
        GtkFileChooser *chooser = data;
429
        gint active;
430
        gchar *filename;
431
        gchar *base;
432
        gchar *new_filename;
433
        gchar *p;
434
        FileselFileType *types;
435

    
436
        active = gtk_combo_box_get_active(combo_box);
437
        filename = gtk_file_chooser_get_filename(chooser);
438
        types = g_object_get_data(G_OBJECT(combo_box), "types");
439
        g_print("active: %d filename: %s\n", active, filename);
440
        g_print("type ext: %s\n", types[active].ext);
441
        base = g_path_get_basename(filename);
442
        p = strrchr(base, '.');
443
        if (p)
444
                *p = '\0';
445
        new_filename = g_strconcat(base, ".", types[active].ext, NULL);
446
        g_print("new_filename: %s\n", new_filename);
447
        gtk_file_chooser_set_current_name(chooser, new_filename);
448
        g_free(new_filename);
449
        g_free(base);
450
        g_free(filename);
451
}
452

    
453
#if GTK_CHECK_VERSION(2, 8, 0)
454
static GtkFileChooserConfirmation filesel_confirm_overwrite_cb
455
                                        (GtkFileChooser                *chooser,
456
                                         gpointer                 data)
457
{
458
        gchar *filename;
459
        GtkFileChooserConfirmation ret =
460
                GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
461

    
462
        filename = gtk_file_chooser_get_filename(chooser);
463

    
464
        if (filename && is_file_exist(filename)) {
465
                AlertValue aval;
466

    
467
#ifdef G_OS_WIN32
468
                gchar *target = NULL;
469

    
470
                if (is_ext_lnk(filename) &&
471
                    (target = filesel_get_link(filename)) != NULL &&
472
                    is_dir_exist(target)) {
473
                        gtk_file_chooser_set_current_folder(chooser, target);
474
                            g_free(target);
475
                            g_free(filename);
476
                            return GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
477
                }
478
                g_free(target);
479
#endif
480

    
481
                aval = alertpanel(_("Overwrite existing file"),
482
                                  _("The file already exists. Do you want to replace it?"),
483
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
484
                if (G_ALERTDEFAULT == aval)
485
                        ret = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
486
                else
487
                        ret = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
488
        }
489

    
490
        g_free(filename);
491

    
492
        return ret;
493
}
494
#endif
495

    
496
#ifdef G_OS_WIN32
497
static gboolean is_ext_lnk(const gchar *filename)
498
{
499
        const gchar *ext;
500

    
501
        if (filename && (ext = strrchr(filename, '.')) &&
502
            g_ascii_strcasecmp(ext, ".lnk") == 0)
503
                return TRUE;
504

    
505
        return FALSE;
506
}
507

    
508
static gchar *filesel_get_link(const gchar *link_file)
509
{
510
        WIN32_FIND_DATAW wfd;
511
        IShellLinkW *psl;
512
        IPersistFile *ppf;
513
        HRESULT hr;
514
        wchar_t *wlink_file;
515
        wchar_t wtarget[MAX_PATH];
516
        gchar *target = NULL;
517

    
518
        wtarget[0] = 0L;
519

    
520
        debug_print("link_file: %s\n", link_file);
521

    
522
        CoInitialize(NULL);
523
        if (S_OK == CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (void **)&psl)) {
524
                if (S_OK == IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (void **)&ppf)) {
525
                        wlink_file = g_utf8_to_utf16(link_file, -1, NULL, NULL, NULL);
526
                        if (S_OK == IPersistFile_Load(ppf, wlink_file, STGM_READ)) {
527
                                if (S_OK == IShellLinkW_GetPath(psl, wtarget, MAX_PATH, &wfd, SLGP_UNCPRIORITY)) {
528
                                        target = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
529
                                }
530
                        }
531
                        IPersistFile_Release(ppf);
532
                        g_free(wlink_file);
533
                }
534
                IShellLinkW_Release(psl);
535
        }
536
        CoUninitialize();
537

    
538
        if (target)
539
                debug_print("target: %s\n", target);
540
        else
541
                debug_print("target not found\n");
542

    
543
        return target;
544
}
545

    
546
static GSList *filesel_resolve_link(GtkFileChooser *chooser, GSList *list, gboolean *dir_selected)
547
{
548
        GSList *cur;
549
        GSList *new_list = NULL;
550
        gchar *target;
551

    
552
        for (cur = list; cur != NULL; cur = cur->next) {
553
                gchar *selected = (gchar *)cur->data;
554

    
555
                if (is_ext_lnk(selected)) {
556
                        target = filesel_get_link(selected);
557

    
558
                            if (is_dir_exist(target)) {
559
                                gtk_file_chooser_set_current_folder(chooser, target);
560
                                g_free(target);
561
                                slist_free_strings(new_list);
562
                                g_slist_free(new_list);
563
                                    new_list = NULL;
564
                                *dir_selected = TRUE;
565
                                    break;
566
                        } else if (is_file_exist(target)) {
567
                                new_list = g_slist_append(new_list, target);
568
                        } else {
569
                                    g_free(target);
570
                        }
571
                } else {
572
                        new_list = g_slist_append(new_list, g_strdup(selected));
573
                }
574
        }
575

    
576
        slist_free_strings(list);
577
        g_slist_free(list);
578

    
579
        return new_list;
580
}
581
#endif