/* * fm-file-menu.c * * Copyright 2009 PCMan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "fm.h" #include "fm-config.h" #include "fm-file-menu.h" #include "fm-path.h" #include "fm-clipboard.h" #include "fm-file-properties.h" #include "fm-utils.h" #include "fm-gtk-utils.h" #include "fm-app-chooser-dlg.h" #include "fm-archiver.h" static void on_open(GtkAction* action, gpointer user_data); static void on_open_with_app(GtkAction* action, gpointer user_data); static void on_open_with(GtkAction* action, gpointer user_data); static void on_cut(GtkAction* action, gpointer user_data); static void on_copy(GtkAction* action, gpointer user_data); static void on_paste(GtkAction* action, gpointer user_data); static void on_delete(GtkAction* action, gpointer user_data); static void on_untrash(GtkAction* action, gpointer user_data); static void on_rename(GtkAction* action, gpointer user_data); static void on_compress(GtkAction* action, gpointer user_data); static void on_extract_here(GtkAction* action, gpointer user_data); static void on_extract_to(GtkAction* action, gpointer user_data); static void on_prop(GtkAction* action, gpointer user_data); const char base_menu_xml[]= "" "" "" "" "" "" "" "" "" "" "" "" "" /* TODO: implement symlink creation and "send to". "" "" "" */ "" "" "" "" ""; /* FIXME: how to show accel keys in the popup menu? */ GtkActionEntry base_menu_actions[]= { {"Open", GTK_STOCK_OPEN, NULL, NULL, NULL, G_CALLBACK(on_open)}, {"OpenWith", NULL, N_("Open With..."), NULL, NULL, G_CALLBACK(on_open_with)}, {"OpenWithMenu", NULL, N_("Open With..."), NULL, NULL, NULL}, {"Cut", GTK_STOCK_CUT, NULL, "X", NULL, G_CALLBACK(on_cut)}, {"Copy", GTK_STOCK_COPY, NULL, "C", NULL, G_CALLBACK(on_copy)}, {"Paste", GTK_STOCK_PASTE, NULL, "V", NULL, G_CALLBACK(on_paste)}, {"Del", GTK_STOCK_DELETE, NULL, NULL, NULL, G_CALLBACK(on_delete)}, {"Rename", NULL, N_("Rename"), "F2", NULL, G_CALLBACK(on_rename)}, {"Link", NULL, N_("Create Symlink"), NULL, NULL, NULL}, {"SendTo", NULL, N_("Send To"), NULL, NULL, NULL}, {"Compress", NULL, N_("Compress..."), NULL, NULL, G_CALLBACK(on_compress)}, {"Extract", NULL, N_("Extract Here"), NULL, NULL, G_CALLBACK(on_extract_here)}, {"Extract2", NULL, N_("Extract To..."), NULL, NULL, G_CALLBACK(on_extract_to)}, {"Prop", GTK_STOCK_PROPERTIES, NULL, NULL, NULL, G_CALLBACK(on_prop)} }; void fm_file_menu_destroy(FmFileMenu* menu) { if(menu->menu) gtk_widget_destroy(menu->menu); if(menu->file_infos) fm_list_unref(menu->file_infos); if(menu->cwd) fm_path_unref(menu->cwd); g_object_unref(menu->act_grp); g_object_unref(menu->ui); g_slice_free(FmFileMenu, menu); } FmFileMenu* fm_file_menu_new_for_file(FmFileInfo* fi, FmPath* cwd, gboolean auto_destroy) { FmFileMenu* menu; FmFileInfoList* files = fm_file_info_list_new(); fm_list_push_tail(files, fi); menu = fm_file_menu_new_for_files(files, cwd, auto_destroy); fm_list_unref(files); return menu; } FmFileMenu* fm_file_menu_new_for_files(FmFileInfoList* files, FmPath* cwd, gboolean auto_destroy) { GtkWidget* menu; GtkUIManager* ui; GtkActionGroup* act_grp; GtkAccelGroup* accel_grp; GtkAction* act; FmFileInfo* fi; FmFileMenu* data = g_slice_new0(FmFileMenu); GString* xml; data->auto_destroy = auto_destroy; data->ui = ui = gtk_ui_manager_new(); data->act_grp = act_grp = gtk_action_group_new("Popup"); gtk_action_group_set_translation_domain(act_grp, GETTEXT_PACKAGE); data->file_infos = fm_list_ref(files); if(cwd) data->cwd = fm_path_ref(cwd); gtk_action_group_add_actions(act_grp, base_menu_actions, G_N_ELEMENTS(base_menu_actions), data); gtk_ui_manager_add_ui_from_string(ui, base_menu_xml, -1, NULL); gtk_ui_manager_insert_action_group(ui, act_grp, 0); /* check if the files are of the same type */ data->same_type = fm_file_info_list_is_same_type(files); xml = g_string_new(""); if(data->same_type) /* add specific menu items for this mime type */ { fi = (FmFileInfo*)fm_list_peek_head(files); if(fi->type) { GList* apps = g_app_info_get_all_for_type(fi->type->type); GList* l; gboolean use_sub = g_list_length(apps) > 5; if(use_sub) g_string_append(xml, ""); for(l=apps;l;l=l->next) { GAppInfo* app = l->data; act = gtk_action_new(g_app_info_get_id(app), g_app_info_get_name(app), g_app_info_get_description(app), NULL); g_signal_connect(act, "activate", G_CALLBACK(on_open_with_app), data); gtk_action_set_gicon(act, g_app_info_get_icon(app)); gtk_action_group_add_action(act_grp, act); /* associate the app info object with the action */ g_object_set_qdata_full(G_OBJECT(act), fm_qdata_id, app, (GDestroyNotify)g_object_unref); g_string_append_printf(xml, "", g_app_info_get_id(app)); } g_list_free(apps); if(use_sub) { g_string_append(xml, "" "" ""); } else g_string_append(xml, ""); } } else g_string_append(xml, ""); g_string_append(xml, ""); /* archiver integration */ g_string_append(xml, ""); if(data->same_type) { FmArchiver* archiver = fm_archiver_get_default(); if(archiver) { fi = (FmFileInfo*)fm_list_peek_head(files); if(fm_archiver_is_mime_type_supported(archiver, fi->type->type)) { if(data->cwd && archiver->extract_to_cmd) g_string_append(xml, ""); if(archiver->extract_cmd) g_string_append(xml, ""); } else g_string_append(xml, ""); } } else g_string_append(xml, ""); g_string_append(xml, ""); /* Special handling for some virtual filesystems */ g_string_append(xml, ""); if(fm_file_info_list_is_same_fs(files)) { fi = (FmFileInfo*)fm_list_peek_head(files); if(fm_path_is_virtual(fi->path)) { /* if all of the files are all in trash */ if(fm_path_is_trash(fi->path)) { gboolean can_restore = TRUE; GList* l; /* only immediate children of trash:/// can be restored. */ for(l = fm_list_peek_head_link(files);l;l=l->next) { FmPath* trash_path = FM_FILE_INFO(l->data)->path; if(!trash_path->parent || !fm_path_is_trash_root(trash_path->parent)) { can_restore = FALSE; break; } } if(can_restore) { act = gtk_action_new("UnTrash", _("_Restore"), _("Restore trashed files to original paths"), NULL); g_signal_connect(act, "activate", G_CALLBACK(on_untrash), data); gtk_action_group_add_action(act_grp, act); g_string_append(xml, ""); } } else { g_debug("%s", fi->fs_id); } } } g_string_append(xml, ""); gtk_ui_manager_add_ui_from_string(ui, xml->str, xml->len, NULL); g_string_free(xml, TRUE); return data; } GtkUIManager* fm_file_menu_get_ui(FmFileMenu* menu) { return menu->ui; } GtkActionGroup* fm_file_menu_get_action_group(FmFileMenu* menu) { return menu->act_grp; } FmFileInfoList* fm_file_menu_get_file_info_list(FmFileMenu* menu) { return menu->file_infos; } /* build the menu with GtkUIManager */ GtkMenu* fm_file_menu_get_menu(FmFileMenu* menu) { if( ! menu->menu ) { menu->menu = gtk_ui_manager_get_widget(menu->ui, "/popup"); if(menu->auto_destroy) g_signal_connect_swapped(menu->menu, "selection-done", G_CALLBACK(fm_file_menu_destroy), menu); } return menu->menu; } void on_open(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; GList* l = fm_list_peek_head_link(data->file_infos); GError* err = NULL; fm_launch_files_simple(GTK_WINDOW(gtk_widget_get_toplevel(data->menu)), NULL, l, data->folder_func, data->folder_func_data); } static void open_with_app(FmFileMenu* data, GAppInfo* app) { GdkAppLaunchContext* ctx; FmFileInfoList* files = data->file_infos; GList* l = fm_list_peek_head_link(files); GList* uris = NULL; int i; for(i=0; l; ++i, l=l->next) { FmFileInfo* fi = (FmFileInfo*)l->data; FmPath* path = fi->path; char* uri = fm_path_to_uri(path); uris = g_list_prepend(uris, uri); } uris = g_list_reverse(uris); ctx = gdk_app_launch_context_new(); gdk_app_launch_context_set_screen(ctx, gtk_widget_get_screen(data->menu)); gdk_app_launch_context_set_icon(ctx, g_app_info_get_icon(app)); gdk_app_launch_context_set_timestamp(ctx, gtk_get_current_event_time()); /* FIXME: error handling. */ g_app_info_launch_uris(app, uris, ctx, NULL); g_object_unref(ctx); g_list_foreach(uris, (GFunc)g_free, NULL); g_list_free(uris); } void on_open_with_app(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; GAppInfo* app = (GAppInfo*)g_object_get_qdata(G_OBJECT(action), fm_qdata_id); g_debug("%s", gtk_action_get_name(action)); open_with_app(data, app); } void on_open_with(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmFileInfoList* files = data->file_infos; FmFileInfo* fi = (FmFileInfo*)fm_list_peek_head(files); FmMimeType* mime_type; GAppInfo* app; if(data->same_type && fi->type && fi->type->type) mime_type = fi->type; else mime_type = NULL; app = fm_choose_app_for_mime_type(NULL, mime_type, TRUE); if(app) { open_with_app(data, app); g_object_unref(app); } } void on_cut(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmPathList* files; files = fm_path_list_new_from_file_info_list(data->file_infos); fm_clipboard_cut_files(data->menu, files); fm_list_unref(files); } void on_copy(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmPathList* files; files = fm_path_list_new_from_file_info_list(data->file_infos); fm_clipboard_copy_files(data->menu, files); fm_list_unref(files); } void on_paste(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; /* fm_clipboard_paste_files(data->menu, ); */ } void on_delete(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmPathList* files; files = fm_path_list_new_from_file_info_list(data->file_infos); fm_trash_or_delete_files(files); fm_list_unref(files); } void on_untrash(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmPathList* files; files = fm_path_list_new_from_file_info_list(data->file_infos); fm_untrash_files(files); fm_list_unref(files); } void on_rename(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmFileInfo* fi = fm_list_peek_head(data->file_infos); if(fi) fm_rename_file(fi->path); /* FIXME: is it ok to only rename the first selected file here. */ /* FmPathList* files; files = fm_path_list_new_from_file_info_list(data->file_infos); if( !fm_list_is_empty(files) ) fm_delete_files(files); fm_list_unref(files); */ } void on_compress(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmPathList* files; GAppLaunchContext* ctx = gdk_app_launch_context_new(); FmArchiver* archiver = fm_archiver_get_default(); files = fm_path_list_new_from_file_info_list(data->file_infos); fm_archiver_create_archive(archiver, ctx, files); fm_list_unref(files); g_object_unref(ctx); } void on_extract_here(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmPathList* files; GAppLaunchContext* ctx = gdk_app_launch_context_new(); FmArchiver* archiver = fm_archiver_get_default(); files = fm_path_list_new_from_file_info_list(data->file_infos); fm_archiver_extract_archives_to(archiver, ctx, files, data->cwd); fm_list_unref(files); g_object_unref(ctx); } void on_extract_to(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; FmPathList* files; GAppLaunchContext* ctx = gdk_app_launch_context_new(); FmArchiver* archiver = fm_archiver_get_default(); files = fm_path_list_new_from_file_info_list(data->file_infos); fm_archiver_extract_archives(archiver, ctx, files); fm_list_unref(files); g_object_unref(ctx); } void on_prop(GtkAction* action, gpointer user_data) { FmFileMenu* data = (FmFileMenu*)user_data; fm_show_file_properties(data->file_infos); } gboolean fm_file_menu_is_single_file_type(FmFileMenu* menu) { return menu->same_type; } void fm_file_menu_set_folder_func(FmFileMenu* menu, FmLaunchFolderFunc func, gpointer user_data) { menu->folder_func = func; menu->folder_func_data = user_data; }