~noskcaj/ubuntu/utopic/libfm/merge

« back to all changes in this revision

Viewing changes to src/gtk/fm-file-menu.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee (李健秋)
  • Date: 2010-04-29 03:52:06 UTC
  • Revision ID: james.westby@ubuntu.com-20100429035206-qlj1jwsfcgr5mdx3
Tags: upstream-0.1.11
Import upstream version 0.1.11

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      fm-file-menu.c
 
3
 *
 
4
 *      Copyright 2009 PCMan <pcman.tw@gmail.com>
 
5
 *
 
6
 *      This program is free software; you can redistribute it and/or modify
 
7
 *      it under the terms of the GNU General Public License as published by
 
8
 *      the Free Software Foundation; either version 2 of the License, or
 
9
 *      (at your option) any later version.
 
10
 *
 
11
 *      This program is distributed in the hope that it will be useful,
 
12
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *      GNU General Public License for more details.
 
15
 *
 
16
 *      You should have received a copy of the GNU General Public License
 
17
 *      along with this program; if not, write to the Free Software
 
18
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
19
 *      MA 02110-1301, USA.
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#include <glib/gi18n-lib.h>
 
27
 
 
28
#include "fm.h"
 
29
#include "fm-config.h"
 
30
 
 
31
#include "fm-file-menu.h"
 
32
#include "fm-path.h"
 
33
 
 
34
#include "fm-clipboard.h"
 
35
#include "fm-file-properties.h"
 
36
#include "fm-utils.h"
 
37
#include "fm-gtk-utils.h"
 
38
#include "fm-app-chooser-dlg.h"
 
39
#include "fm-archiver.h"
 
40
 
 
41
static void on_open(GtkAction* action, gpointer user_data);
 
42
static void on_open_with_app(GtkAction* action, gpointer user_data);
 
43
static void on_open_with(GtkAction* action, gpointer user_data);
 
44
static void on_cut(GtkAction* action, gpointer user_data);
 
45
static void on_copy(GtkAction* action, gpointer user_data);
 
46
static void on_paste(GtkAction* action, gpointer user_data);
 
47
static void on_delete(GtkAction* action, gpointer user_data);
 
48
static void on_untrash(GtkAction* action, gpointer user_data);
 
49
static void on_rename(GtkAction* action, gpointer user_data);
 
50
static void on_compress(GtkAction* action, gpointer user_data);
 
51
static void on_extract_here(GtkAction* action, gpointer user_data);
 
52
static void on_extract_to(GtkAction* action, gpointer user_data);
 
53
static void on_prop(GtkAction* action, gpointer user_data);
 
54
 
 
55
const char base_menu_xml[]=
 
56
"<popup>"
 
57
  "<menuitem action='Open'/>"
 
58
  "<separator/>"
 
59
  "<placeholder name='ph1'/>"
 
60
  "<separator/>"
 
61
  "<placeholder name='ph2'/>"
 
62
  "<separator/>"
 
63
  "<menuitem action='Cut'/>"
 
64
  "<menuitem action='Copy'/>"
 
65
  "<menuitem action='Paste'/>"
 
66
  "<menuitem action='Del'/>"
 
67
  "<separator/>"
 
68
  "<menuitem action='Rename'/>"
 
69
/* TODO: implement symlink creation and "send to".
 
70
  "<menuitem action='Link'/>"
 
71
  "<menu action='SendTo'>"
 
72
  "</menu>"
 
73
*/
 
74
  "<separator/>"
 
75
  "<placeholder name='ph3'/>"
 
76
  "<separator/>"
 
77
  "<menuitem action='Prop'/>"
 
78
"</popup>";
 
79
 
 
80
/* FIXME: how to show accel keys in the popup menu? */
 
81
GtkActionEntry base_menu_actions[]=
 
82
{
 
83
    {"Open", GTK_STOCK_OPEN, NULL, NULL, NULL, G_CALLBACK(on_open)},
 
84
    {"OpenWith", NULL, N_("Open With..."), NULL, NULL, G_CALLBACK(on_open_with)},
 
85
    {"OpenWithMenu", NULL, N_("Open With..."), NULL, NULL, NULL},
 
86
    {"Cut", GTK_STOCK_CUT, NULL, "<Ctrl>X", NULL, G_CALLBACK(on_cut)},
 
87
    {"Copy", GTK_STOCK_COPY, NULL, "<Ctrl>C", NULL, G_CALLBACK(on_copy)},
 
88
    {"Paste", GTK_STOCK_PASTE, NULL, "<Ctrl>V", NULL, G_CALLBACK(on_paste)},
 
89
    {"Del", GTK_STOCK_DELETE, NULL, NULL, NULL, G_CALLBACK(on_delete)},
 
90
    {"Rename", NULL, N_("Rename"), "F2", NULL, G_CALLBACK(on_rename)},
 
91
    {"Link", NULL, N_("Create Symlink"), NULL, NULL, NULL},
 
92
    {"SendTo", NULL, N_("Send To"), NULL, NULL, NULL},
 
93
    {"Compress", NULL, N_("Compress..."), NULL, NULL, G_CALLBACK(on_compress)},
 
94
    {"Extract", NULL, N_("Extract Here"), NULL, NULL, G_CALLBACK(on_extract_here)},
 
95
    {"Extract2", NULL, N_("Extract To..."), NULL, NULL, G_CALLBACK(on_extract_to)},
 
96
    {"Prop", GTK_STOCK_PROPERTIES, NULL, NULL, NULL, G_CALLBACK(on_prop)}
 
97
};
 
98
 
 
99
void fm_file_menu_destroy(FmFileMenu* menu)
 
100
{
 
101
    if(menu->menu)
 
102
        gtk_widget_destroy(menu->menu);
 
103
 
 
104
    if(menu->file_infos)
 
105
        fm_list_unref(menu->file_infos);
 
106
 
 
107
    if(menu->cwd)
 
108
        fm_path_unref(menu->cwd);
 
109
 
 
110
    g_object_unref(menu->act_grp);
 
111
    g_object_unref(menu->ui);
 
112
    g_slice_free(FmFileMenu, menu);
 
113
}
 
114
 
 
115
FmFileMenu* fm_file_menu_new_for_file(FmFileInfo* fi, FmPath* cwd, gboolean auto_destroy)
 
116
{
 
117
    FmFileMenu* menu;
 
118
    FmFileInfoList* files = fm_file_info_list_new();
 
119
    fm_list_push_tail(files, fi);
 
120
    menu = fm_file_menu_new_for_files(files, cwd, auto_destroy);
 
121
    fm_list_unref(files);
 
122
    return menu;
 
123
}
 
124
 
 
125
FmFileMenu* fm_file_menu_new_for_files(FmFileInfoList* files, FmPath* cwd, gboolean auto_destroy)
 
126
{
 
127
    GtkWidget* menu;
 
128
    GtkUIManager* ui;
 
129
    GtkActionGroup* act_grp;
 
130
    GtkAccelGroup* accel_grp;
 
131
    GtkAction* act;
 
132
    FmFileInfo* fi;
 
133
    FmFileMenu* data = g_slice_new0(FmFileMenu);
 
134
    GString* xml;
 
135
 
 
136
    data->auto_destroy = auto_destroy;
 
137
    data->ui = ui = gtk_ui_manager_new();
 
138
    data->act_grp = act_grp = gtk_action_group_new("Popup");
 
139
    gtk_action_group_set_translation_domain(act_grp, GETTEXT_PACKAGE);
 
140
 
 
141
    data->file_infos = fm_list_ref(files);
 
142
    if(cwd)
 
143
        data->cwd = fm_path_ref(cwd);
 
144
 
 
145
    gtk_action_group_add_actions(act_grp, base_menu_actions, G_N_ELEMENTS(base_menu_actions), data);
 
146
    gtk_ui_manager_add_ui_from_string(ui, base_menu_xml, -1, NULL);
 
147
    gtk_ui_manager_insert_action_group(ui, act_grp, 0);
 
148
 
 
149
    /* check if the files are of the same type */
 
150
    data->same_type = fm_file_info_list_is_same_type(files);
 
151
 
 
152
    xml = g_string_new("<popup><placeholder name='ph2'>");
 
153
    if(data->same_type) /* add specific menu items for this mime type */
 
154
    {
 
155
        fi = (FmFileInfo*)fm_list_peek_head(files);
 
156
        if(fi->type)
 
157
        {
 
158
            GList* apps = g_app_info_get_all_for_type(fi->type->type);
 
159
            GList* l;
 
160
            gboolean use_sub = g_list_length(apps) > 5;
 
161
            if(use_sub)
 
162
                g_string_append(xml, "<menu action='OpenWithMenu'>");
 
163
 
 
164
            for(l=apps;l;l=l->next)
 
165
            {
 
166
                GAppInfo* app = l->data;
 
167
                act = gtk_action_new(g_app_info_get_id(app),
 
168
                            g_app_info_get_name(app),
 
169
                            g_app_info_get_description(app),
 
170
                            NULL);
 
171
                g_signal_connect(act, "activate", G_CALLBACK(on_open_with_app), data);
 
172
                gtk_action_set_gicon(act, g_app_info_get_icon(app));
 
173
                gtk_action_group_add_action(act_grp, act);
 
174
                /* associate the app info object with the action */
 
175
                g_object_set_qdata_full(G_OBJECT(act), fm_qdata_id, app, (GDestroyNotify)g_object_unref);
 
176
                g_string_append_printf(xml, "<menuitem action='%s'/>", g_app_info_get_id(app));
 
177
            }
 
178
 
 
179
            g_list_free(apps);
 
180
            if(use_sub)
 
181
            {
 
182
                g_string_append(xml,
 
183
                    "<separator/>"
 
184
                    "<menuitem action='OpenWith'/>"
 
185
                    "</menu>");
 
186
            }
 
187
            else
 
188
                g_string_append(xml, "<menuitem action='OpenWith'/>");
 
189
        }
 
190
    }
 
191
    else
 
192
        g_string_append(xml, "<menuitem action='OpenWith'/>");
 
193
    g_string_append(xml, "</placeholder></popup>");
 
194
 
 
195
    /* archiver integration */
 
196
    g_string_append(xml, "<popup><placeholder name='ph3'>");
 
197
    if(data->same_type)
 
198
    {
 
199
        FmArchiver* archiver = fm_archiver_get_default();
 
200
        if(archiver)
 
201
        {
 
202
            fi = (FmFileInfo*)fm_list_peek_head(files);
 
203
            if(fm_archiver_is_mime_type_supported(archiver, fi->type->type))
 
204
            {
 
205
                if(data->cwd && archiver->extract_to_cmd)
 
206
                    g_string_append(xml, "<menuitem action='Extract'/>");
 
207
                if(archiver->extract_cmd)
 
208
                    g_string_append(xml, "<menuitem action='Extract2'/>");
 
209
            }
 
210
            else
 
211
                g_string_append(xml, "<menuitem action='Compress'/>");
 
212
        }
 
213
    }
 
214
    else
 
215
        g_string_append(xml, "<menuitem action='Compress'/>");
 
216
    g_string_append(xml, "</placeholder></popup>");
 
217
 
 
218
    /* Special handling for some virtual filesystems */
 
219
    g_string_append(xml, "<popup><placeholder name='ph1'>");
 
220
    if(fm_file_info_list_is_same_fs(files))
 
221
    {
 
222
        fi = (FmFileInfo*)fm_list_peek_head(files);
 
223
        if(fm_path_is_virtual(fi->path))
 
224
        {
 
225
            /* if all of the files are all in trash */
 
226
            if(fm_path_is_trash(fi->path))
 
227
            {
 
228
                gboolean can_restore = TRUE;
 
229
                GList* l;
 
230
                /* only immediate children of trash:/// can be restored. */
 
231
                for(l = fm_list_peek_head_link(files);l;l=l->next)
 
232
                {
 
233
                    FmPath* trash_path = FM_FILE_INFO(l->data)->path;
 
234
                    if(!trash_path->parent || !fm_path_is_trash_root(trash_path->parent))
 
235
                    {
 
236
                        can_restore = FALSE;
 
237
                        break;
 
238
                    }
 
239
                }
 
240
 
 
241
                if(can_restore)
 
242
                {
 
243
                    act = gtk_action_new("UnTrash",
 
244
                                        _("_Restore"),
 
245
                                        _("Restore trashed files to original paths"),
 
246
                                NULL);
 
247
                    g_signal_connect(act, "activate", G_CALLBACK(on_untrash), data);
 
248
                    gtk_action_group_add_action(act_grp, act);
 
249
                    g_string_append(xml, "<menuitem action='UnTrash'/>");
 
250
                }
 
251
            }
 
252
            else
 
253
            {
 
254
                g_debug("%s", fi->fs_id);
 
255
            }
 
256
        }
 
257
    }
 
258
    g_string_append(xml, "</placeholder></popup>");
 
259
 
 
260
    gtk_ui_manager_add_ui_from_string(ui, xml->str, xml->len, NULL);
 
261
 
 
262
    g_string_free(xml, TRUE);
 
263
    return data;
 
264
}
 
265
 
 
266
GtkUIManager* fm_file_menu_get_ui(FmFileMenu* menu)
 
267
{
 
268
    return menu->ui;
 
269
}
 
270
 
 
271
GtkActionGroup* fm_file_menu_get_action_group(FmFileMenu* menu)
 
272
{
 
273
    return menu->act_grp;
 
274
}
 
275
 
 
276
FmFileInfoList* fm_file_menu_get_file_info_list(FmFileMenu* menu)
 
277
{
 
278
    return menu->file_infos;
 
279
}
 
280
 
 
281
/* build the menu with GtkUIManager */
 
282
GtkMenu* fm_file_menu_get_menu(FmFileMenu* menu)
 
283
{
 
284
    if( ! menu->menu )
 
285
    {
 
286
        menu->menu = gtk_ui_manager_get_widget(menu->ui, "/popup");
 
287
        if(menu->auto_destroy)
 
288
            g_signal_connect_swapped(menu->menu, "selection-done",
 
289
                            G_CALLBACK(fm_file_menu_destroy), menu);
 
290
    }
 
291
    return menu->menu;
 
292
}
 
293
 
 
294
void on_open(GtkAction* action, gpointer user_data)
 
295
{
 
296
    FmFileMenu* data = (FmFileMenu*)user_data;
 
297
    GList* l = fm_list_peek_head_link(data->file_infos);
 
298
    GError* err = NULL;
 
299
    fm_launch_files_simple(GTK_WINDOW(gtk_widget_get_toplevel(data->menu)), NULL, l, data->folder_func, data->folder_func_data);
 
300
}
 
301
 
 
302
static void open_with_app(FmFileMenu* data, GAppInfo* app)
 
303
{
 
304
    GdkAppLaunchContext* ctx;
 
305
    FmFileInfoList* files = data->file_infos;
 
306
    GList* l = fm_list_peek_head_link(files);
 
307
    GList* uris = NULL;
 
308
    int i;
 
309
    for(i=0; l; ++i, l=l->next)
 
310
    {
 
311
        FmFileInfo* fi = (FmFileInfo*)l->data;
 
312
        FmPath* path = fi->path;
 
313
        char* uri = fm_path_to_uri(path);
 
314
        uris = g_list_prepend(uris, uri);
 
315
    }
 
316
    uris = g_list_reverse(uris);
 
317
 
 
318
    ctx = gdk_app_launch_context_new();
 
319
    gdk_app_launch_context_set_screen(ctx, gtk_widget_get_screen(data->menu));
 
320
    gdk_app_launch_context_set_icon(ctx, g_app_info_get_icon(app));
 
321
    gdk_app_launch_context_set_timestamp(ctx, gtk_get_current_event_time());
 
322
 
 
323
    /* FIXME: error handling. */
 
324
    g_app_info_launch_uris(app, uris, ctx, NULL);
 
325
    g_object_unref(ctx);
 
326
 
 
327
    g_list_foreach(uris, (GFunc)g_free, NULL);
 
328
    g_list_free(uris);
 
329
}
 
330
 
 
331
void on_open_with_app(GtkAction* action, gpointer user_data)
 
332
{
 
333
    FmFileMenu* data = (FmFileMenu*)user_data;
 
334
    GAppInfo* app = (GAppInfo*)g_object_get_qdata(G_OBJECT(action), fm_qdata_id);
 
335
    g_debug("%s", gtk_action_get_name(action));
 
336
    open_with_app(data, app);
 
337
}
 
338
 
 
339
void on_open_with(GtkAction* action, gpointer user_data)
 
340
{
 
341
    FmFileMenu* data = (FmFileMenu*)user_data;
 
342
    FmFileInfoList* files = data->file_infos;
 
343
    FmFileInfo* fi = (FmFileInfo*)fm_list_peek_head(files);
 
344
    FmMimeType* mime_type;
 
345
    GAppInfo* app;
 
346
 
 
347
    if(data->same_type && fi->type && fi->type->type)
 
348
        mime_type = fi->type;
 
349
    else
 
350
        mime_type = NULL;
 
351
 
 
352
    app = fm_choose_app_for_mime_type(NULL, mime_type, TRUE);
 
353
 
 
354
    if(app)
 
355
    {
 
356
        open_with_app(data, app);
 
357
        g_object_unref(app);
 
358
    }
 
359
}
 
360
 
 
361
void on_cut(GtkAction* action, gpointer user_data)
 
362
{
 
363
    FmFileMenu* data = (FmFileMenu*)user_data;
 
364
    FmPathList* files;
 
365
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
366
    fm_clipboard_cut_files(data->menu, files);
 
367
    fm_list_unref(files);
 
368
}
 
369
 
 
370
void on_copy(GtkAction* action, gpointer user_data)
 
371
{
 
372
    FmFileMenu* data = (FmFileMenu*)user_data;
 
373
    FmPathList* files;
 
374
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
375
    fm_clipboard_copy_files(data->menu, files);
 
376
    fm_list_unref(files);
 
377
}
 
378
 
 
379
void on_paste(GtkAction* action, gpointer user_data)
 
380
{
 
381
    FmFileMenu* data = (FmFileMenu*)user_data;
 
382
    /* fm_clipboard_paste_files(data->menu, ); */
 
383
}
 
384
 
 
385
void on_delete(GtkAction* action, gpointer user_data)
 
386
{
 
387
    FmFileMenu* data = (FmFileMenu*)user_data;
 
388
    FmPathList* files;
 
389
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
390
    fm_trash_or_delete_files(files);
 
391
    fm_list_unref(files);
 
392
}
 
393
 
 
394
void on_untrash(GtkAction* action, gpointer user_data)
 
395
{
 
396
    FmFileMenu* data = (FmFileMenu*)user_data;
 
397
    FmPathList* files;
 
398
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
399
    fm_untrash_files(files);
 
400
    fm_list_unref(files);
 
401
}
 
402
 
 
403
void on_rename(GtkAction* action, gpointer user_data)
 
404
{
 
405
    FmFileMenu* data = (FmFileMenu*)user_data;
 
406
    FmFileInfo* fi = fm_list_peek_head(data->file_infos);
 
407
    if(fi)
 
408
        fm_rename_file(fi->path);
 
409
    /* FIXME: is it ok to only rename the first selected file here. */
 
410
/*
 
411
    FmPathList* files;
 
412
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
413
    if( !fm_list_is_empty(files) )
 
414
        fm_delete_files(files);
 
415
    fm_list_unref(files);
 
416
*/
 
417
}
 
418
 
 
419
void on_compress(GtkAction* action, gpointer user_data)
 
420
{
 
421
    FmFileMenu* data = (FmFileMenu*)user_data;
 
422
    FmPathList* files;
 
423
    GAppLaunchContext* ctx = gdk_app_launch_context_new();
 
424
    FmArchiver* archiver = fm_archiver_get_default();
 
425
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
426
    fm_archiver_create_archive(archiver, ctx, files);
 
427
    fm_list_unref(files);
 
428
    g_object_unref(ctx);
 
429
}
 
430
 
 
431
void on_extract_here(GtkAction* action, gpointer user_data)
 
432
{
 
433
    FmFileMenu* data = (FmFileMenu*)user_data;
 
434
    FmPathList* files;
 
435
    GAppLaunchContext* ctx = gdk_app_launch_context_new();
 
436
    FmArchiver* archiver = fm_archiver_get_default();
 
437
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
438
    fm_archiver_extract_archives_to(archiver, ctx, files, data->cwd);
 
439
    fm_list_unref(files);
 
440
    g_object_unref(ctx);
 
441
}
 
442
 
 
443
void on_extract_to(GtkAction* action, gpointer user_data)
 
444
{
 
445
    FmFileMenu* data = (FmFileMenu*)user_data;
 
446
    FmPathList* files;
 
447
    GAppLaunchContext* ctx = gdk_app_launch_context_new();
 
448
    FmArchiver* archiver = fm_archiver_get_default();
 
449
    files = fm_path_list_new_from_file_info_list(data->file_infos);
 
450
    fm_archiver_extract_archives(archiver, ctx, files);
 
451
    fm_list_unref(files);
 
452
    g_object_unref(ctx);
 
453
}
 
454
 
 
455
void on_prop(GtkAction* action, gpointer user_data)
 
456
{
 
457
    FmFileMenu* data = (FmFileMenu*)user_data;
 
458
    fm_show_file_properties(data->file_infos);
 
459
}
 
460
 
 
461
gboolean fm_file_menu_is_single_file_type(FmFileMenu* menu)
 
462
{
 
463
    return menu->same_type;
 
464
}
 
465
 
 
466
void fm_file_menu_set_folder_func(FmFileMenu* menu, FmLaunchFolderFunc func, gpointer user_data)
 
467
{
 
468
    menu->folder_func = func;
 
469
    menu->folder_func_data = user_data;
 
470
}
 
471
 
 
472