4
* Copyright 2009 PCMan <pcman.tw@gmail.com>
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.
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.
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,
26
#include <glib/gi18n-lib.h>
29
#include "fm-config.h"
31
#include "fm-file-menu.h"
34
#include "fm-clipboard.h"
35
#include "fm-file-properties.h"
37
#include "fm-gtk-utils.h"
38
#include "fm-app-chooser-dlg.h"
39
#include "fm-archiver.h"
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);
55
const char base_menu_xml[]=
57
"<menuitem action='Open'/>"
59
"<placeholder name='ph1'/>"
61
"<placeholder name='ph2'/>"
63
"<menuitem action='Cut'/>"
64
"<menuitem action='Copy'/>"
65
"<menuitem action='Paste'/>"
66
"<menuitem action='Del'/>"
68
"<menuitem action='Rename'/>"
69
/* TODO: implement symlink creation and "send to".
70
"<menuitem action='Link'/>"
71
"<menu action='SendTo'>"
75
"<placeholder name='ph3'/>"
77
"<menuitem action='Prop'/>"
80
/* FIXME: how to show accel keys in the popup menu? */
81
GtkActionEntry base_menu_actions[]=
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)}
99
void fm_file_menu_destroy(FmFileMenu* menu)
102
gtk_widget_destroy(menu->menu);
105
fm_list_unref(menu->file_infos);
108
fm_path_unref(menu->cwd);
110
g_object_unref(menu->act_grp);
111
g_object_unref(menu->ui);
112
g_slice_free(FmFileMenu, menu);
115
FmFileMenu* fm_file_menu_new_for_file(FmFileInfo* fi, FmPath* cwd, gboolean auto_destroy)
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);
125
FmFileMenu* fm_file_menu_new_for_files(FmFileInfoList* files, FmPath* cwd, gboolean auto_destroy)
129
GtkActionGroup* act_grp;
130
GtkAccelGroup* accel_grp;
133
FmFileMenu* data = g_slice_new0(FmFileMenu);
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);
141
data->file_infos = fm_list_ref(files);
143
data->cwd = fm_path_ref(cwd);
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);
149
/* check if the files are of the same type */
150
data->same_type = fm_file_info_list_is_same_type(files);
152
xml = g_string_new("<popup><placeholder name='ph2'>");
153
if(data->same_type) /* add specific menu items for this mime type */
155
fi = (FmFileInfo*)fm_list_peek_head(files);
158
GList* apps = g_app_info_get_all_for_type(fi->type->type);
160
gboolean use_sub = g_list_length(apps) > 5;
162
g_string_append(xml, "<menu action='OpenWithMenu'>");
164
for(l=apps;l;l=l->next)
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),
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));
184
"<menuitem action='OpenWith'/>"
188
g_string_append(xml, "<menuitem action='OpenWith'/>");
192
g_string_append(xml, "<menuitem action='OpenWith'/>");
193
g_string_append(xml, "</placeholder></popup>");
195
/* archiver integration */
196
g_string_append(xml, "<popup><placeholder name='ph3'>");
199
FmArchiver* archiver = fm_archiver_get_default();
202
fi = (FmFileInfo*)fm_list_peek_head(files);
203
if(fm_archiver_is_mime_type_supported(archiver, fi->type->type))
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'/>");
211
g_string_append(xml, "<menuitem action='Compress'/>");
215
g_string_append(xml, "<menuitem action='Compress'/>");
216
g_string_append(xml, "</placeholder></popup>");
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))
222
fi = (FmFileInfo*)fm_list_peek_head(files);
223
if(fm_path_is_virtual(fi->path))
225
/* if all of the files are all in trash */
226
if(fm_path_is_trash(fi->path))
228
gboolean can_restore = TRUE;
230
/* only immediate children of trash:/// can be restored. */
231
for(l = fm_list_peek_head_link(files);l;l=l->next)
233
FmPath* trash_path = FM_FILE_INFO(l->data)->path;
234
if(!trash_path->parent || !fm_path_is_trash_root(trash_path->parent))
243
act = gtk_action_new("UnTrash",
245
_("Restore trashed files to original paths"),
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'/>");
254
g_debug("%s", fi->fs_id);
258
g_string_append(xml, "</placeholder></popup>");
260
gtk_ui_manager_add_ui_from_string(ui, xml->str, xml->len, NULL);
262
g_string_free(xml, TRUE);
266
GtkUIManager* fm_file_menu_get_ui(FmFileMenu* menu)
271
GtkActionGroup* fm_file_menu_get_action_group(FmFileMenu* menu)
273
return menu->act_grp;
276
FmFileInfoList* fm_file_menu_get_file_info_list(FmFileMenu* menu)
278
return menu->file_infos;
281
/* build the menu with GtkUIManager */
282
GtkMenu* fm_file_menu_get_menu(FmFileMenu* menu)
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);
294
void on_open(GtkAction* action, gpointer user_data)
296
FmFileMenu* data = (FmFileMenu*)user_data;
297
GList* l = fm_list_peek_head_link(data->file_infos);
299
fm_launch_files_simple(GTK_WINDOW(gtk_widget_get_toplevel(data->menu)), NULL, l, data->folder_func, data->folder_func_data);
302
static void open_with_app(FmFileMenu* data, GAppInfo* app)
304
GdkAppLaunchContext* ctx;
305
FmFileInfoList* files = data->file_infos;
306
GList* l = fm_list_peek_head_link(files);
309
for(i=0; l; ++i, l=l->next)
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);
316
uris = g_list_reverse(uris);
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());
323
/* FIXME: error handling. */
324
g_app_info_launch_uris(app, uris, ctx, NULL);
327
g_list_foreach(uris, (GFunc)g_free, NULL);
331
void on_open_with_app(GtkAction* action, gpointer user_data)
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);
339
void on_open_with(GtkAction* action, gpointer user_data)
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;
347
if(data->same_type && fi->type && fi->type->type)
348
mime_type = fi->type;
352
app = fm_choose_app_for_mime_type(NULL, mime_type, TRUE);
356
open_with_app(data, app);
361
void on_cut(GtkAction* action, gpointer user_data)
363
FmFileMenu* data = (FmFileMenu*)user_data;
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);
370
void on_copy(GtkAction* action, gpointer user_data)
372
FmFileMenu* data = (FmFileMenu*)user_data;
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);
379
void on_paste(GtkAction* action, gpointer user_data)
381
FmFileMenu* data = (FmFileMenu*)user_data;
382
/* fm_clipboard_paste_files(data->menu, ); */
385
void on_delete(GtkAction* action, gpointer user_data)
387
FmFileMenu* data = (FmFileMenu*)user_data;
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);
394
void on_untrash(GtkAction* action, gpointer user_data)
396
FmFileMenu* data = (FmFileMenu*)user_data;
398
files = fm_path_list_new_from_file_info_list(data->file_infos);
399
fm_untrash_files(files);
400
fm_list_unref(files);
403
void on_rename(GtkAction* action, gpointer user_data)
405
FmFileMenu* data = (FmFileMenu*)user_data;
406
FmFileInfo* fi = fm_list_peek_head(data->file_infos);
408
fm_rename_file(fi->path);
409
/* FIXME: is it ok to only rename the first selected file here. */
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);
419
void on_compress(GtkAction* action, gpointer user_data)
421
FmFileMenu* data = (FmFileMenu*)user_data;
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);
431
void on_extract_here(GtkAction* action, gpointer user_data)
433
FmFileMenu* data = (FmFileMenu*)user_data;
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);
443
void on_extract_to(GtkAction* action, gpointer user_data)
445
FmFileMenu* data = (FmFileMenu*)user_data;
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);
455
void on_prop(GtkAction* action, gpointer user_data)
457
FmFileMenu* data = (FmFileMenu*)user_data;
458
fm_show_file_properties(data->file_infos);
461
gboolean fm_file_menu_is_single_file_type(FmFileMenu* menu)
463
return menu->same_type;
466
void fm_file_menu_set_folder_func(FmFileMenu* menu, FmLaunchFolderFunc func, gpointer user_data)
468
menu->folder_func = func;
469
menu->folder_func_data = user_data;