Statistics
| Revision:

root / src / filesel.c @ 3245

History | View | Annotate | Download (13.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
                                         GSList                        *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
#if GTK_CHECK_VERSION(2, 8, 0)
62
static GtkFileChooserConfirmation filesel_confirm_overwrite_cb
63
                                        (GtkFileChooser                *chooser,
64
                                         gpointer                 data);
65
#endif
66
#ifdef G_OS_WIN32
67
static gboolean is_ext_lnk                (const gchar                *filename);
68
static gchar *filesel_get_link                (const gchar                *link_file);
69
static GSList *filesel_resolve_link        (GtkFileChooser                *chooser,
70
                                         GSList                        *list,
71
                                         gboolean                *dir_selected);
72
#endif
73

    
74

    
75
gchar *filesel_select_file(const gchar *title, const gchar *file,
76
                           GtkFileChooserAction action)
77
{
78
        GSList *list;
79
        gchar *selected = NULL;
80

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

    
88
        return selected;
89
}
90

    
91
GSList *filesel_select_files(const gchar *title, const gchar *file,
92
                             GtkFileChooserAction action)
93
{
94
        return filesel_select_file_full(title, file, action, TRUE, NULL, 0, NULL);
95
}
96

    
97
static void filesel_change_dir_for_action(GtkFileChooserAction action)
98
{
99
        const gchar *cwd = NULL;
100

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

    
133
        if (cwd)
134
                change_dir(cwd);
135
        else
136
                change_dir(get_document_dir());
137
}
138

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

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

    
173
        prev_dir = g_get_current_dir();
174

    
175
        filesel_change_dir_for_action(action);
176

    
177
        dialog = filesel_create(title, action);
178

    
179
        manage_window_set_transient(GTK_WINDOW(dialog));
180

    
181
        if (file)
182
                gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
183
                                                  file);
184

    
185
        gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),
186
                                             multiple);
187

    
188
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE && save_expander_expanded) {
189
                filesel_save_expander_set_expanded
190
                        (dialog, save_expander_expanded);
191
        }
192

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

    
203
        /* create types combo box */
204
        if (types) {
205
                GSList *cur;
206
                GtkWidget *hbox;
207
                GtkWidget *label;
208

    
209
                hbox = gtk_hbox_new(FALSE, 12);
210
                label = gtk_label_new(_("File type:"));
211
                gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
212

    
213
                combo = gtk_combo_box_new_text();
214
                for (cur = types; cur != NULL; cur = cur->next) {
215
                        gtk_combo_box_append_text(GTK_COMBO_BOX(combo), (const gchar *)cur->data);
216
                }
217
                gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
218

    
219
                gtk_widget_show_all(hbox);
220
                gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), hbox);
221
                gtk_combo_box_set_active(GTK_COMBO_BOX(combo), default_type);
222
        }
223

    
224
        gtk_widget_show(dialog);
225

    
226
        change_dir(prev_dir);
227
        g_free(prev_dir);
228

    
229
        inc_lock();
230

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

    
259
        inc_unlock();
260

    
261
        if (combo && selected_type) {
262
                *selected_type = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
263
        }
264

    
265
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
266
                save_expander_expanded =
267
                        filesel_save_expander_get_expanded(dialog);
268

    
269
        manage_window_focus_out(dialog, NULL, NULL);
270
        gtk_widget_destroy(dialog);
271

    
272
        return list;
273
}
274

    
275
gchar *filesel_save_as(const gchar *file)
276
{
277
        gchar *filename;
278

    
279
        filename = filesel_select_file(_("Save as"), file,
280
                                       GTK_FILE_CHOOSER_ACTION_SAVE);
281

    
282
#if !GTK_CHECK_VERSION(2, 8, 0)
283
        if (filename && is_file_exist(filename)) {
284
                AlertValue aval;
285

    
286
                aval = alertpanel(_("Overwrite existing file"),
287
                                  _("The file already exists. Do you want to replace it?"),
288
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
289
                if (G_ALERTDEFAULT != aval) {
290
                        g_free(filename);
291
                        filename = NULL;
292
                }
293
        }
294
#endif
295

    
296
        return filename;
297
}
298

    
299
gchar *filesel_save_as_type(const gchar *file, GSList *types,
300
                            gint default_type, gint *selected_type)
301
{
302
        GSList *list;
303
        gchar *filename = NULL;
304

    
305
        list = filesel_select_file_full(_("Save as"), file,
306
                                        GTK_FILE_CHOOSER_ACTION_SAVE, FALSE,
307
                                        types, default_type, selected_type);
308
        if (list) {
309
                filename = (gchar *)list->data;
310
                slist_free_strings(list->next);
311
        }
312
        g_slist_free(list);
313

    
314
#if !GTK_CHECK_VERSION(2, 8, 0)
315
        if (filename && is_file_exist(filename)) {
316
                AlertValue aval;
317

    
318
                aval = alertpanel(_("Overwrite existing file"),
319
                                  _("The file already exists. Do you want to replace it?"),
320
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
321
                if (G_ALERTDEFAULT != aval) {
322
                        g_free(filename);
323
                        filename = NULL;
324
                }
325
        }
326
#endif
327

    
328
        return filename;
329
}
330

    
331
gchar *filesel_select_dir(const gchar *dir)
332
{
333
        GSList *list;
334
        gchar *selected = NULL;
335

    
336
        list = filesel_select_file_full(_("Select folder"), dir,
337
                                        GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
338
                                        FALSE, NULL, 0, NULL);
339
        if (list) {
340
                selected = (gchar *)list->data;
341
                slist_free_strings(list->next);
342
        }
343
        g_slist_free(list);
344

    
345
        return selected;
346
}
347

    
348
static GtkWidget *filesel_create(const gchar *title,
349
                                 GtkFileChooserAction action)
350
{
351
        GtkWidget *dialog;
352

    
353
        if (prefs_common.comply_gnome_hig)
354
                dialog = gtk_file_chooser_dialog_new
355
                        (title, NULL, action,
356
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
357
                         (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
358
                          action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
359
                         ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
360
                         NULL);
361
        else
362
                dialog = gtk_file_chooser_dialog_new
363
                        (title, NULL, action,
364
                         (action == GTK_FILE_CHOOSER_ACTION_SAVE ||
365
                          action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
366
                         ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
367
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
368
                         NULL);
369
        gtk_window_set_position(GTK_WINDOW(dialog),
370
                                GTK_WIN_POS_CENTER_ON_PARENT);
371
        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
372
        gtk_window_set_wmclass
373
                (GTK_WINDOW(dialog), "file_selection", "Sylpheed");
374
        gtk_dialog_set_default_response
375
                (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
376

    
377
        MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
378

    
379
        return dialog;
380
}
381

    
382
static void container_foreach_cb(GtkWidget *widget, gpointer data)
383
{
384
        GtkWidget **expander = (GtkWidget **)data;
385

    
386
        if (*expander == NULL) {
387
                if (GTK_IS_EXPANDER(widget))
388
                        *expander = widget;
389
                else if (GTK_IS_CONTAINER(widget))
390
                        gtk_container_foreach(GTK_CONTAINER(widget),
391
                                              container_foreach_cb, data);
392
        }
393
}
394

    
395
static void filesel_save_expander_set_expanded(GtkWidget *dialog,
396
                                               gboolean expanded)
397
{
398
        GtkWidget *expander = NULL;
399

    
400
        gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
401
                              &expander);
402
        if (expander)
403
                gtk_expander_set_expanded(GTK_EXPANDER(expander), expanded);
404
}
405

    
406
static gboolean filesel_save_expander_get_expanded(GtkWidget *dialog)
407
{
408
        GtkWidget *expander = NULL;
409

    
410
        gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb,
411
                              &expander);
412
        if (expander)
413
                return gtk_expander_get_expanded(GTK_EXPANDER(expander));
414
        else
415
                return FALSE;
416
}
417

    
418
#if GTK_CHECK_VERSION(2, 8, 0)
419
static GtkFileChooserConfirmation filesel_confirm_overwrite_cb
420
                                        (GtkFileChooser                *chooser,
421
                                         gpointer                 data)
422
{
423
        gchar *filename;
424
        GtkFileChooserConfirmation ret =
425
                GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
426

    
427
        filename = gtk_file_chooser_get_filename(chooser);
428

    
429
        if (filename && is_file_exist(filename)) {
430
                AlertValue aval;
431

    
432
#ifdef G_OS_WIN32
433
                gchar *target = NULL;
434

    
435
                if (is_ext_lnk(filename) &&
436
                    (target = filesel_get_link(filename)) != NULL &&
437
                    is_dir_exist(target)) {
438
                        gtk_file_chooser_set_current_folder(chooser, target);
439
                            g_free(target);
440
                            g_free(filename);
441
                            return GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
442
                }
443
                g_free(target);
444
#endif
445

    
446
                aval = alertpanel(_("Overwrite existing file"),
447
                                  _("The file already exists. Do you want to replace it?"),
448
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
449
                if (G_ALERTDEFAULT == aval)
450
                        ret = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
451
                else
452
                        ret = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
453
        }
454

    
455
        g_free(filename);
456

    
457
        return ret;
458
}
459
#endif
460

    
461
#ifdef G_OS_WIN32
462
static gboolean is_ext_lnk(const gchar *filename)
463
{
464
        const gchar *ext;
465

    
466
        if (filename && (ext = strrchr(filename, '.')) &&
467
            g_ascii_strcasecmp(ext, ".lnk") == 0)
468
                return TRUE;
469

    
470
        return FALSE;
471
}
472

    
473
static gchar *filesel_get_link(const gchar *link_file)
474
{
475
        WIN32_FIND_DATAW wfd;
476
        IShellLinkW *psl;
477
        IPersistFile *ppf;
478
        HRESULT hr;
479
        wchar_t *wlink_file;
480
        wchar_t wtarget[MAX_PATH];
481
        gchar *target = NULL;
482

    
483
        wtarget[0] = 0L;
484

    
485
        debug_print("link_file: %s\n", link_file);
486

    
487
        CoInitialize(NULL);
488
        if (S_OK == CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (void **)&psl)) {
489
                if (S_OK == IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (void **)&ppf)) {
490
                        wlink_file = g_utf8_to_utf16(link_file, -1, NULL, NULL, NULL);
491
                        if (S_OK == IPersistFile_Load(ppf, wlink_file, STGM_READ)) {
492
                                if (S_OK == IShellLinkW_GetPath(psl, wtarget, MAX_PATH, &wfd, SLGP_UNCPRIORITY)) {
493
                                        target = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
494
                                }
495
                        }
496
                        IPersistFile_Release(ppf);
497
                        g_free(wlink_file);
498
                }
499
                IShellLinkW_Release(psl);
500
        }
501
        CoUninitialize();
502

    
503
        if (target)
504
                debug_print("target: %s\n", target);
505
        else
506
                debug_print("target not found\n");
507

    
508
        return target;
509
}
510

    
511
static GSList *filesel_resolve_link(GtkFileChooser *chooser, GSList *list, gboolean *dir_selected)
512
{
513
        GSList *cur;
514
        GSList *new_list = NULL;
515
        gchar *target;
516

    
517
        for (cur = list; cur != NULL; cur = cur->next) {
518
                gchar *selected = (gchar *)cur->data;
519

    
520
                if (is_ext_lnk(selected)) {
521
                        target = filesel_get_link(selected);
522

    
523
                            if (is_dir_exist(target)) {
524
                                gtk_file_chooser_set_current_folder(chooser, target);
525
                                g_free(target);
526
                                slist_free_strings(new_list);
527
                                g_slist_free(new_list);
528
                                    new_list = NULL;
529
                                *dir_selected = TRUE;
530
                                    break;
531
                        } else if (is_file_exist(target)) {
532
                                new_list = g_slist_append(new_list, target);
533
                        } else {
534
                                    g_free(target);
535
                        }
536
                } else {
537
                        new_list = g_slist_append(new_list, g_strdup(selected));
538
                }
539
        }
540

    
541
        slist_free_strings(list);
542
        g_slist_free(list);
543

    
544
        return new_list;
545
}
546
#endif