/** * @file gnome-cmd-user-actions.cc * @copyright (C) 2001-2006 Marcus Bjurman\n * @copyright (C) 2007-2012 Piotr Eljasiak\n * @copyright (C) 2013-2023 Uwe Scholz\n * * @copyright 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. * * @copyright 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. * * @copyright 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 St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "gnome-cmd-includes.h" #include "gnome-cmd-con-home.h" #include "gnome-cmd-con-list.h" #include "gnome-cmd-data.h" #include "gnome-cmd-dir.h" #include "gnome-cmd-file-list.h" #include "gnome-cmd-file-selector.h" #include "gnome-cmd-main-win.h" #include "gnome-cmd-user-actions.h" #include "gnome-cmd-dir-indicator.h" #include "gnome-cmd-style.h" #include "plugin_manager.h" #include "cap.h" #include "utils.h" #include "dialogs/gnome-cmd-advrename-dialog.h" #include "dialogs/gnome-cmd-chmod-dialog.h" #include "dialogs/gnome-cmd-chown-dialog.h" #include "dialogs/gnome-cmd-con-dialog.h" #include "dialogs/gnome-cmd-remote-dialog.h" #include "dialogs/gnome-cmd-key-shortcuts-dialog.h" #include "dialogs/gnome-cmd-make-copy-dialog.h" #include "dialogs/gnome-cmd-manage-bookmarks-dialog.h" #include "dialogs/gnome-cmd-mkdir-dialog.h" #include "dialogs/gnome-cmd-search-dialog.h" #include "dialogs/gnome-cmd-options-dialog.h" #include "dialogs/gnome-cmd-prepare-copy-dialog.h" #include "dialogs/gnome-cmd-prepare-move-dialog.h" using namespace std; /*********************************** * Functions for using GSettings ***********************************/ struct _GcmdUserActionSettings { GObject parent; GSettings *filter; GSettings *general; GSettings *programs; }; G_DEFINE_TYPE (GcmdUserActionSettings, gcmd_user_action_settings, G_TYPE_OBJECT) static void gcmd_user_action_settings_finalize (GObject *object) { G_OBJECT_CLASS (gcmd_user_action_settings_parent_class)->finalize (object); } static void gcmd_user_action_settings_dispose (GObject *object) { GcmdUserActionSettings *gs = GCMD_USER_ACTIONS (object); g_clear_object (&gs->general); g_clear_object (&gs->programs); G_OBJECT_CLASS (gcmd_user_action_settings_parent_class)->dispose (object); } static void gcmd_user_action_settings_class_init (GcmdUserActionSettingsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gcmd_user_action_settings_finalize; object_class->dispose = gcmd_user_action_settings_dispose; } GcmdUserActionSettings *gcmd_user_action_settings_new () { return (GcmdUserActionSettings *) g_object_new (USER_ACTION_SETTINGS, nullptr); } static void gcmd_user_action_settings_init (GcmdUserActionSettings *gs) { GSettingsSchemaSource *global_schema_source; GSettingsSchema *global_schema; global_schema_source = GnomeCmdData::GetGlobalSchemaSource(); global_schema = g_settings_schema_source_lookup (global_schema_source, GCMD_PREF_FILTER, FALSE); gs->filter = g_settings_new_full (global_schema, nullptr, nullptr); global_schema = g_settings_schema_source_lookup (global_schema_source, GCMD_PREF_GENERAL, FALSE); gs->general = g_settings_new_full (global_schema, nullptr, nullptr); global_schema = g_settings_schema_source_lookup (global_schema_source, GCMD_PREF_PROGRAMS, FALSE); gs->programs = g_settings_new_full (global_schema, nullptr, nullptr); } /*********************************** * UserActions ***********************************/ GnomeCmdFileSelector *get_fs (const FileSelectorID fsID) { return main_win->fs(fsID); } GnomeCmdFileList *get_fl (const FileSelectorID fsID) { GnomeCmdFileSelector *fs = get_fs (fsID); return fs ? fs->file_list() : nullptr; } // The file returned from this function is not to be unrefed static GnomeCmdFile *get_selected_file (const FileSelectorID fsID) { GnomeCmdFile *f = get_fl (fsID)->get_first_selected_file(); if (!f) gnome_cmd_show_message (*main_win, _("No file selected")); return f; } inline gboolean append_real_path (string &s, const gchar *name) { s += ' '; s += name; return TRUE; } static gboolean append_real_path (string &s, GnomeCmdFile *f) { if (!f) return FALSE; gchar *name = g_shell_quote (f->get_real_path()); append_real_path (s, name); g_free (name); return TRUE; } int parse_command(string *cmd, gchar * command); GnomeCmdUserActions gcmd_user_actions; inline bool operator < (const GdkEventKey &e1, const GdkEventKey &e2) { if (e1.keyval < e2.keyval) return true; if (e1.keyval > e2.keyval) return false; return (e1.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK)) < (e2.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK)); } DICT GnomeCmdUserActions::action_func; DICT GnomeCmdUserActions::action_name; struct UserActionData { GnomeCmdUserActionFunc func; const gchar *name; const gchar *description; }; static UserActionData user_actions_data[] = { {bookmarks_add_current, "bookmarks.add_current", N_("Bookmark current directory")}, {bookmarks_edit, "bookmarks.edit", N_("Manage bookmarks")}, {bookmarks_goto, "bookmarks.goto", N_("Go to bookmarked location")}, {bookmarks_view, "bookmarks.view", N_("Show bookmarks of current device")}, {command_execute, "command.execute", N_("Execute command")}, {command_open_terminal, "command.open_terminal", N_("Open terminal")}, {command_open_terminal_as_root, "command.open_terminal_as_root", N_("Open terminal as root")}, {command_root_mode, "command.root_mode", N_("Start GNOME Commander as root")}, {connections_close_current, "connections.close", N_("Close connection")}, {connections_new, "connections.new", N_("New connection")}, {connections_open, "connections.open", N_("Open connection")}, {connections_change_left, "connections.change_left", N_("Change left connection")}, {connections_change_right, "connections.change_right", N_("Change right connection")}, {edit_cap_copy, "edit.copy", N_("Copy")}, {edit_copy_fnames, "edit.copy_filenames", N_("Copy file names")}, {edit_cap_cut, "edit.cut", N_("Cut")}, {file_delete, "edit.delete", N_("Delete")}, {edit_filter, "edit.filter", N_("Show user defined files")}, {edit_cap_paste, "edit.paste", N_("Paste")}, {file_quick_search, "file.quick_search", N_("Quick search")}, {file_search, "file.search", N_("Search")}, {file_advrename, "file.advrename", N_("Advanced rename tool")}, {file_chmod, "file.chmod", N_("Change permissions")}, {file_chown, "file.chown", N_("Change owner/group")}, {file_copy, "file.copy", N_("Copy files")}, {file_copy_as, "file.copy_as", N_("Copy files with rename")}, {file_create_symlink, "file.create_symlink", N_("Create symbolic link")}, {file_delete, "file.delete", N_("Delete files")}, {file_diff, "file.diff", N_("Compare files (diff)")}, {file_edit, "file.edit", N_("Edit file")}, {file_edit_new_doc, "file.edit_new_doc", N_("Edit a new file")}, {file_exit, "file.exit", N_("Quit")}, {file_external_view, "file.external_view", N_("View with external viewer")}, {file_internal_view, "file.internal_view", N_("View with internal viewer")}, {file_mkdir, "file.mkdir", N_("Create directory")}, {file_move, "file.move", N_("Move files")}, {file_properties, "file.properties", N_("Properties")}, {file_rename, "file.rename", N_("Rename files")}, // {file_run, "file.run"}, {file_sendto, "file.sendto", N_("Send files")}, {file_sync_dirs, "file.synchronize_directories", N_("Synchronize directories")}, // {file_umount, "file.umount"}, {file_view, "file.view", N_("View file")}, {help_about, "help.about", N_("About GNOME Commander")}, {help_help, "help.help", N_("Help contents")}, {help_keyboard, "help.keyboard", N_("Help on keyboard shortcuts")}, {help_problem, "help.problem", N_("Report a problem")}, {help_web, "help.web", N_("GNOME Commander on the web")}, {mark_compare_directories, "mark.compare_directories", N_("Compare directories")}, {mark_invert_selection, "mark.invert", N_("Invert selection")}, {mark_select_all, "mark.select_all", N_("Select all")}, {mark_select_all_files, "mark.select_all_files", N_("Select all files")}, {mark_unselect_all_files, "mark.unselect_all_files", N_("Unselect all files")}, {mark_toggle, "mark.toggle", N_("Toggle selection")}, {mark_toggle_and_step, "mark.toggle_and_step", N_("Toggle selection and move cursor downward")}, {mark_unselect_all, "mark.unselect_all", N_("Unselect all")}, {no_action, "no.action", N_("Do nothing")}, {options_edit, "options.edit", N_("Options")}, {options_edit_shortcuts, "options.shortcuts", N_("Keyboard shortcuts")}, {plugins_configure, "plugins.configure", N_("Configure plugins")}, {view_back, "view.back", N_("Back one directory")}, {view_close_tab, "view.close_tab", N_("Close the current tab")}, {view_close_all_tabs, "view.close_all_tabs", N_("Close all tabs")}, {view_close_duplicate_tabs, "view.close_duplicate_tabs", N_("Close duplicate tabs")}, {view_directory, "view.directory", N_("Change directory")}, {view_dir_history, "view.dir_history", N_("Show directory history")}, {view_equal_panes, "view.equal_panes", N_("Equal panel size")}, {view_maximize_pane, "view.maximize_pane", N_("Maximize panel size")}, {view_first, "view.first", N_("Back to the first directory")}, {view_forward, "view.forward", N_("Forward one directory")}, {view_home, "view.home", N_("Home directory")}, {view_in_active_pane, "view.in_active_pane", N_("Open directory in the active window")}, {view_in_inactive_pane, "view.in_inactive_pane", N_("Open directory in the inactive window")}, {view_in_left_pane, "view.in_left_pane", N_("Open directory in the left window")}, {view_in_right_pane, "view.in_right_pane", N_("Open directory in the right window")}, {view_in_new_tab, "view.in_new_tab", N_("Open directory in the new tab")}, {view_in_inactive_tab, "view.in_inactive_tab", N_("Open directory in the new tab (inactive window)")}, {view_last, "view.last", N_("Forward to the last directory")}, {view_next_tab, "view.next_tab", N_("Next tab")}, {view_new_tab, "view.new_tab", N_("Open directory in a new tab")}, {view_prev_tab, "view.prev_tab", N_("Previous tab")}, {view_refresh, "view.refresh", N_("Refresh")}, {view_root, "view.root", N_("Root directory")}, {view_toggle_tab_lock, "view.toggle_lock_tab", N_("Lock/unlock tab")}, #if 0 {view_terminal, "view.terminal", N_("Show terminal")}, #endif {view_up, "view.up", N_("Up one directory")}, {view_main_menu, "view.main_menu", N_("Display main menu")}, {view_step_up, "view.step_up", N_("Move cursor one step up")}, {view_step_down, "view.step_down", N_("Move cursor one step down")}, }; void GnomeCmdUserActions::init() { for (guint i=0; i::iterator pos = action.find(event); if (pos!=action.end()) action.erase(pos); } gboolean GnomeCmdUserActions::registered(const gchar *the_action_name) { GnomeCmdUserActionFunc func = action_func[the_action_name]; if (!func) return FALSE; for (ACTIONS_COLL::const_iterator i=action.begin(); i!=action.end(); ++i) if (i->second.func==func) return TRUE; return FALSE; } gboolean GnomeCmdUserActions::registered(guint state, guint keyval) { GdkEventKey event; event.keyval = keyval; event.state = state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK); return action.find(event)!=action.end(); } gboolean GnomeCmdUserActions::handle_key_event(GnomeCmdMainWin *mw, GnomeCmdFileList *fl, GdkEventKey *event) { map ::const_iterator pos = action.find(*event); if (pos==action.end()) return FALSE; DEBUG('u', "Key event: %s (%#x)\n", key2str(*event).c_str(), event->keyval); DEBUG('u', "Handling key event by %s()\n", action_func[pos->second.func].c_str()); (*pos->second.func) (nullptr, (gpointer) (pos->second.user_data.empty() ? nullptr : pos->second.user_data.c_str())); return TRUE; } static int sort_by_description (const void *data1, const void *data2) { const gchar *s1 = (static_cast (data1))->description; const gchar *s2 = (static_cast (data2))->description; if (!s1 && !s2) return 0; if (!s1) return 1; if (!s2) return -1; // compare s1 and s2 in UTF8 aware way, case insensitive gchar *is1 = g_utf8_casefold (_(s1), -1); gchar *is2 = g_utf8_casefold (_(s2), -1); gint retval = g_utf8_collate (is1, is2); g_free (is1); g_free (is2); return retval; } GtkTreeModel *gnome_cmd_user_actions_create_model () { auto data = static_cast (g_memdup (user_actions_data, sizeof(user_actions_data))); qsort (data, G_N_ELEMENTS(user_actions_data), sizeof(UserActionData), sort_by_description); GtkListStore *model = gtk_list_store_new (3, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING); GtkTreeIter iter; for (guint i=0; i static void get_file_list (string &s, GList *sfl, F f) { vector a; for (GList *i = sfl; i; i = i->next) { auto value = (*f) (GNOME_CMD_FILE (i->data)); if (value) a.push_back (value); } join (s, a.begin(), a.end()); } template inline void get_file_list (string &s, GList *sfl, F f, T t) { vector a; for (GList *i = sfl; i; i = i->next) { auto value = (*f) (GNOME_CMD_FILE (i->data), t); if (value) a.push_back (value); } join (s, a.begin(), a.end()); } /***************************************/ void no_action (GtkMenuItem *menuitem, gpointer not_used) { } /************** File Menu **************/ void file_copy (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *src_fs = get_fs (ACTIVE); GnomeCmdFileSelector *dest_fs = get_fs (INACTIVE); if (src_fs && dest_fs) gnome_cmd_prepare_copy_dialog_show (src_fs, dest_fs); } void file_copy_as (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFile *f = fs->file_list()->get_selected_file(); if (GNOME_CMD_IS_FILE (f)) { GtkWidget *dialog = gnome_cmd_make_copy_dialog_new (f, fs->get_directory()); g_object_ref (dialog); gtk_widget_show (dialog); } } void file_move (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *src_fs = get_fs (ACTIVE); GnomeCmdFileSelector *dest_fs = get_fs (INACTIVE); if (src_fs && dest_fs) gnome_cmd_prepare_move_dialog_show (src_fs, dest_fs); } void file_delete (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_show_delete_dialog (get_fl (ACTIVE)); } void file_view (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_view (get_fl (ACTIVE), gnome_cmd_data.options.use_internal_viewer); } void file_internal_view (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_view (get_fl (ACTIVE), TRUE); } void file_external_view (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_view (get_fl (ACTIVE), FALSE); } void file_edit (GtkMenuItem *menuitem, gpointer not_used) { GdkModifierType mask; gdk_window_get_pointer (nullptr, nullptr, nullptr, &mask); if (mask & GDK_SHIFT_MASK) gnome_cmd_file_selector_show_new_textfile_dialog (get_fs (ACTIVE)); else { gchar *command; command = g_strdup (gnome_cmd_data.options.editor); g_return_if_fail (command != nullptr); string cmd; cmd.reserve(2000); if (parse_command(&cmd, (const gchar*) command) == 0) { DEBUG ('g', "Edit file command is not valid.\n"); gnome_cmd_show_message (*main_win, _("No valid command given.")); return; } else { gint argc; gchar **argv = nullptr; GError *error = nullptr; DEBUG ('g', "Edit file: %s\n", cmd.c_str()); g_shell_parse_argv (cmd.c_str(), &argc, &argv, nullptr); if (!g_spawn_async (nullptr, argv, nullptr, G_SPAWN_SEARCH_PATH, nullptr, nullptr, nullptr, &error)) gnome_cmd_error_message (_("Unable to execute command."), error); g_strfreev (argv); g_free (command); } } } void file_edit_new_doc (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_selector_show_new_textfile_dialog (get_fs (ACTIVE)); } void file_search (GtkMenuItem *menuitem, gpointer not_used) { if (gnome_cmd_data.options.use_internal_search) { if (!main_win->file_search_dlg) main_win->file_search_dlg = new GnomeCmdSearchDialog(gnome_cmd_data.search_defaults); main_win->file_search_dlg->show_and_set_focus(); } else { string commandString; commandString.reserve(2000); if (parse_command(&commandString, (const gchar*) gnome_cmd_data.options.search) == 0) { DEBUG ('g', "Search command is empty.\n"); gnome_cmd_show_message (*main_win, _("No search command given."), _("You can set a command for a search tool in the program options.")); return; } else { gint argc; gchar **argv = nullptr; GError *error = nullptr; DEBUG ('g', "Invoking search: %s\n", commandString.c_str()); // put command into argv g_shell_parse_argv (commandString.c_str(), &argc, &argv, nullptr); // execute command if (!g_spawn_async (nullptr, argv, nullptr, G_SPAWN_SEARCH_PATH, nullptr, nullptr, nullptr, &error)) { gnome_cmd_error_message (_("Unable to execute command."), error); } g_strfreev (argv); } } } void file_quick_search (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_show_quicksearch (get_fl (ACTIVE), 0); } void file_chmod (GtkMenuItem *menuitem, gpointer not_used) { GList *files = get_fl (ACTIVE)->get_selected_files(); if (files) { GtkWidget *dialog = gnome_cmd_chmod_dialog_new (files); g_object_ref (dialog); gtk_widget_show (dialog); g_list_free (files); } } void file_chown (GtkMenuItem *menuitem, gpointer not_used) { GList *files = get_fl (ACTIVE)->get_selected_files(); if (files) { GtkWidget *dialog = gnome_cmd_chown_dialog_new (files); g_object_ref (dialog); gtk_widget_show (dialog); g_list_free (files); } } void file_mkdir (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdDir *dir = fs->get_directory(); g_return_if_fail (GNOME_CMD_IS_DIR (dir)); gnome_cmd_dir_ref (dir); gnome_cmd_mkdir_dialog_new (dir, fs->file_list()->get_selected_file()); gnome_cmd_dir_unref (dir); } void file_create_symlink (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *inactive_fs = get_fs (INACTIVE); GList *f = get_fl (ACTIVE)->get_selected_files(); guint selected_files = g_list_length (f); if (selected_files > 1) { gchar *msg = g_strdup_printf (ngettext("Create symbolic links of %i file in %s?", "Create symbolic links of %i files in %s?", selected_files), selected_files, gnome_cmd_dir_get_display_path (inactive_fs->get_directory())); gint choice = run_simple_dialog (*main_win, TRUE, GTK_MESSAGE_QUESTION, msg, _("Create Symbolic Link"), 1, _("Cancel"), _("Create"), nullptr); g_free (msg); if (choice==1) gnome_cmd_file_selector_create_symlinks (inactive_fs, f); } else { GnomeCmdFile *focused_f = get_fl (ACTIVE)->get_focused_file(); gnome_cmd_file_selector_create_symlink (inactive_fs, focused_f); } } void file_rename (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_show_rename_dialog (get_fl (ACTIVE)); } void file_advrename (GtkMenuItem *menuitem, gpointer not_used) { GList *files = get_fl (ACTIVE)->get_selected_files(); if (files) { files = get_fl (ACTIVE)->sort_selection(files); if (!main_win->advrename_dlg) { main_win->advrename_dlg = new GnomeCmdAdvrenameDialog(gnome_cmd_data.advrename_defaults); // g_object_ref (main_win->advrename_dlg); // FIXME: ??? main_win->advrename_dlg->set(files); gtk_widget_show_all (*main_win->advrename_dlg); } else { main_win->advrename_dlg->set(files); gtk_widget_show (*main_win->advrename_dlg); // gdk_window_raise (gtk_widget_get_window (GTK_WIDGET (main_win->advrename_dlg))); // FIXME: bring dlg to top ??? } g_list_free (files); } } void file_sendto (GtkMenuItem *menuitem, gpointer not_used) { string commandString; commandString.reserve(2000); if (parse_command(&commandString, (const gchar*) gnome_cmd_data.options.sendto) == 0) { DEBUG ('g', "Sendto command is not valid.\n"); gnome_cmd_show_message (*main_win, _("No valid command given.")); return; } else { gint argc; gchar **argv = nullptr; GError *error = nullptr; eventually_warn_if_xdg_email_is_used(); DEBUG ('g', "Invoking 'Send files': %s\n", commandString.c_str()); g_shell_parse_argv (commandString.c_str(), &argc, &argv, nullptr); if (!g_spawn_async (nullptr, argv, nullptr, G_SPAWN_SEARCH_PATH, nullptr, nullptr, nullptr, &error)) { gnome_cmd_error_message (_("Unable to execute command."), error); } g_strfreev (argv); } } void eventually_warn_if_xdg_email_is_used() { auto fileList = get_fl (ACTIVE); GList *selectedFileList = fileList->get_selected_files(); auto currentSendToString = g_settings_get_string (gcmd_user_actions.settings->programs, GCMD_SETTINGS_SENDTO_CMD); if ((g_strcmp0(currentSendToString, "xdg-email --attach %s") == 0) && g_list_length(selectedFileList) > 1) { gnome_cmd_show_message (*main_win, _("Warning"), _("The default send-to command only supports one selected file at a time. You can change the command in the program options.")); } g_list_free(selectedFileList); g_free(currentSendToString); } void file_properties (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_show_properties_dialog (get_fl (ACTIVE)); } void file_diff (GtkMenuItem *menuitem, gpointer not_used) { if (!get_fs (ACTIVE)->is_local()) { gnome_cmd_show_message (*main_win, _("Operation not supported on remote file systems")); return; } GnomeCmdFileList *active_fl = get_fl (ACTIVE); GList *sel_files = active_fl->get_selected_files(); string files_to_differ; switch (g_list_length (sel_files)) { case 0: return; case 1: if (!get_fs (INACTIVE)->is_local()) gnome_cmd_show_message (*main_win, _("Operation not supported on remote file systems")); else if (!append_real_path (files_to_differ, get_selected_file (ACTIVE)) || !append_real_path (files_to_differ, get_selected_file (INACTIVE))) files_to_differ.clear(); break; case 2: case 3: sel_files = active_fl->sort_selection(sel_files); for (GList *i = sel_files; i; i = i->next) append_real_path (files_to_differ, GNOME_CMD_FILE (i->data)); break; default: gnome_cmd_show_message (*main_win, _("Too many selected files")); break; } g_list_free (sel_files); if (!files_to_differ.empty()) { #if defined (__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif gchar *cmd = g_strdup_printf (gnome_cmd_data.options.differ, files_to_differ.c_str(), ""); #if defined (__GNUC__) #pragma GCC diagnostic pop #endif run_command (cmd); g_free (cmd); } } void file_sync_dirs (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *active_fs = get_fs (ACTIVE); GnomeCmdFileSelector *inactive_fs = get_fs (INACTIVE); if (!active_fs->is_local() || !inactive_fs->is_local()) { gnome_cmd_show_message (*main_win, _("Operation not supported on remote file systems")); return; } string s; append_real_path (s, GNOME_CMD_FILE (active_fs->get_directory())); append_real_path (s, GNOME_CMD_FILE (inactive_fs->get_directory())); #if defined (__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif gchar *cmd = g_strdup_printf (gnome_cmd_data.options.differ, s.c_str(), ""); #if defined (__GNUC__) #pragma GCC diagnostic pop #endif run_command (cmd); g_free (cmd); } void file_exit (GtkMenuItem *menuitem, gpointer not_used) { gint x, y; #if defined (__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" #endif switch (gnome_cmd_data.main_win_state) { case GDK_WINDOW_STATE_MAXIMIZED: case GDK_WINDOW_STATE_FULLSCREEN: case GDK_WINDOW_STATE_ICONIFIED: break; default: gdk_window_get_root_origin (gtk_widget_get_window (GTK_WIDGET (main_win)), &x, &y); gnome_cmd_data_set_main_win_pos (x, y); break; } #if defined (__GNUC__) #pragma GCC diagnostic pop #endif gtk_widget_destroy (*main_win); } /************** Edit Menu **************/ void edit_cap_cut (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_cap_cut (get_fl (ACTIVE)); } void edit_cap_copy (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_cap_copy (get_fl (ACTIVE)); } void edit_cap_paste (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_selector_cap_paste (get_fs (ACTIVE)); } void edit_filter (GtkMenuItem *menuitem, gpointer not_used) { get_fs (ACTIVE)->show_filter(); } void edit_copy_fnames (GtkMenuItem *menuitem, gpointer fileList) { GdkModifierType mask; GnomeCmdFileList *fl; gdk_window_get_pointer (nullptr, nullptr, nullptr, &mask); if (!GNOME_CMD_IS_FILE_LIST(fileList) || (GnomeCmdFileList*) fileList == nullptr) { fileList = get_fl (ACTIVE); } fl = (GnomeCmdFileList*) fileList; GList *sfl = fl->get_selected_files(); sfl = fl->sort_selection(sfl); string fnames; fnames.reserve(2000); if (state_is_blank (mask)) get_file_list (fnames, sfl, gnome_cmd_file_get_name); else if (state_is_shift (mask)) get_file_list (fnames, sfl, gnome_cmd_file_get_real_path); else if (state_is_alt (mask)) get_file_list (fnames, sfl, gnome_cmd_file_get_uri_str); gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), fnames.c_str(), -1); g_list_free (sfl); } /************** Command Menu **************/ void command_execute (GtkMenuItem *menuitem, gpointer command) { g_return_if_fail (command != nullptr); DEBUG ('g', "invoking: %s\n", command); string dir_path; string quoted_dir_path; string cmd; GnomeCmdDir *dir = nullptr; GnomeCmdFileList *fl = get_fl (ACTIVE); GList *sfl = fl->get_selected_files(); GList *i = sfl = fl->sort_selection(sfl); if (i) { dir = GNOME_CMD_FILE (i->data)->get_parent_dir(); i = i->next; } for (; i && GNOME_CMD_FILE (i->data)->get_parent_dir()==dir; i=i->next); if (i) dir = nullptr; if (dir) { stringify (dir_path, GNOME_CMD_FILE (dir)->get_real_path()); stringify (quoted_dir_path, gnome_cmd_file_get_quoted_real_path (GNOME_CMD_FILE (dir))); } cmd.reserve(2000); if (parse_command(&cmd, (const gchar*) command) == 0) { DEBUG ('g', "Command is not valid.\n"); gnome_cmd_show_message (*main_win, _("No valid command given.")); return; } else { gint argc; gchar **argv; GError *error = nullptr; DEBUG ('g', "Running: %s\n", cmd.c_str()); g_shell_parse_argv (cmd.c_str(), &argc, &argv, nullptr); if (!g_spawn_async (gnome_cmd_dir_is_local (dir) ? dir_path.c_str() : nullptr, argv, nullptr, G_SPAWN_SEARCH_PATH, nullptr, nullptr, nullptr, &error)) gnome_cmd_error_message (_("Unable to execute command."), error); g_strfreev (argv); g_list_free (sfl); } } void command_open_terminal__internal (GtkMenuItem *menuitem, gpointer not_used) // this function is NOT exposed to user as UserAction { GdkModifierType mask; gdk_window_get_pointer (nullptr, nullptr, nullptr, &mask); if (mask & GDK_SHIFT_MASK) command_open_terminal_as_root (menuitem, nullptr); else command_open_terminal (menuitem, nullptr); } /** * Executes the command stored in gnome_cmd_data.options.termopen in the * active directory. */ void command_open_terminal (GtkMenuItem *menuitem, gpointer not_used) { gint argc; gchar **argv; gchar *command; gchar *dpath = GNOME_CMD_FILE (get_fs (ACTIVE)->get_directory())->get_real_path(); GError *error = nullptr; command = g_strdup (gnome_cmd_data.options.termopen); g_return_if_fail (command[0] != '\0'); DEBUG ('g', "running: %s\n", command); g_shell_parse_argv (command, &argc, &argv, nullptr); if (!g_spawn_async (dpath, argv, nullptr, G_SPAWN_SEARCH_PATH, nullptr, nullptr, nullptr, &error)) gnome_cmd_error_message (_("Unable to execute command."), error); g_strfreev (argv); g_free (command); g_free (dpath); } /** * Combines the command stored in gnome_cmd_data.options.termopen with a * command for a GUI for root login screen. The command is executed in * the active directory afterwards. */ void command_open_terminal_as_root (GtkMenuItem *menuitem, gpointer not_used) { int argc = 1; char **argv = g_new0 (char *, argc+1); //initialize argv gchar *command; command = g_strdup (gnome_cmd_data.options.termopen); // Parse the termopen string to get the number of arguments in argc g_shell_parse_argv (command, &argc, &argv, nullptr); g_free (argv); argv = g_new0 (char *, argc+1); argv[0] = command; if (gnome_cmd_prepend_su_to_vector (argc, argv)) { gchar *dpath = GNOME_CMD_FILE (get_fs (ACTIVE)->get_directory())->get_real_path(); GError *error = nullptr; if (!g_spawn_async (dpath, argv, nullptr, G_SPAWN_STDOUT_TO_DEV_NULL, nullptr, nullptr, nullptr, &error)) gnome_cmd_error_message (_("Unable to open terminal in root mode."), error); g_free (dpath); } else gnome_cmd_show_message (nullptr, _("gksudo, xdg-su, gksu, gnomesu, kdesu or beesu is not found.")); g_free (argv); g_free (command); } void command_root_mode (GtkMenuItem *menuitem, gpointer not_used) { int argc = 1; char **argv = g_new0 (char *, argc+1); argv[0] = g_strdup (g_get_prgname ()); if (gnome_cmd_prepend_su_to_vector (argc, argv)) { GError *error = nullptr; if (!g_spawn_async (nullptr, argv, nullptr, G_SPAWN_STDOUT_TO_DEV_NULL, nullptr, nullptr, nullptr, &error)) gnome_cmd_error_message (_("Unable to start GNOME Commander in root mode."), error); } else gnome_cmd_show_message (nullptr, _("gksudo, xdg-su, gksu, gnomesu, kdesu or beesu is not found")); g_strfreev (argv); } /************** Mark Menu **************/ void mark_toggle (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->toggle(); } void mark_toggle_and_step (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->toggle_and_step(); } void mark_select_all (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->select_all(); } void mark_select_all_files (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->select_all_files(); } void mark_unselect_all_files (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->unselect_all_files(); } void mark_unselect_all (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->unselect_all(); } void mark_select_with_pattern (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_show_selpat_dialog (get_fl (ACTIVE), TRUE); } void mark_unselect_with_pattern (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_file_list_show_selpat_dialog (get_fl (ACTIVE), FALSE); } void mark_invert_selection (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->invert_selection(); } void mark_select_all_with_same_extension (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->select_all_with_same_extension(); } void mark_unselect_all_with_same_extension (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->unselect_all_with_same_extension(); } void mark_restore_selection (GtkMenuItem *menuitem, gpointer not_used) { get_fl (ACTIVE)->restore_selection(); } struct ltstr { bool operator() (const char *s1, const char *s2) const { return strcmp(s1,s2) < 0; } }; template struct select2nd { typename T::second_type operator()(T const& value) const { return value.second; } }; template inline select2nd make_select2nd(T const& m) { return select2nd(); } inline void selection_delta(GnomeCmdFileList &fl, set &prev_selection, set &new_selection) { vector a; a.reserve(max(prev_selection.size(),new_selection.size())); set_difference(prev_selection.begin(),prev_selection.end(), new_selection.begin(),new_selection.end(), back_inserter(a)); for (vector::iterator i=a.begin(); i!=a.end(); ++i) fl.unselect_file(*i); a.clear(); set_difference(new_selection.begin(),new_selection.end(), prev_selection.begin(),prev_selection.end(), back_inserter(a)); for (vector::iterator i=a.begin(); i!=a.end(); ++i) fl.select_file(*i); } void mark_compare_directories (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileList *fl1 = get_fl (ACTIVE); GnomeCmdFileList *fl2 = get_fl (INACTIVE); map files2; // map (fname -> GnomeCmdFile *) of visible files (non dirs!) in fl2 for (GList *i2=fl2->get_visible_files(); i2; i2=i2->next) { auto f2 = static_cast (i2->data); if (!f2->is_dotdot && f2->GetGfileAttributeUInt32(G_FILE_ATTRIBUTE_STANDARD_TYPE) != G_FILE_TYPE_DIRECTORY) files2[f2->get_name()] = f2; } set new_selection1, new_selection2; for (GList *i1=fl1->get_visible_files(); i1; i1=i1->next) { auto f1 = static_cast (i1->data); if (f1->is_dotdot || f1->GetGfileAttributeUInt32(G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_DIRECTORY) continue; map::iterator i2 = files2.find(f1->get_name()); if (i2==files2.end()) // if f1 is not found in visible fl2 files... { new_selection1.insert(f1); // ... add f1 to files to be selected in fl1 continue; } GnomeCmdFile *f2 = i2->second; if (f1->GetGfileAttributeUInt64(G_FILE_ATTRIBUTE_TIME_MODIFIED) > f2->GetGfileAttributeUInt64(G_FILE_ATTRIBUTE_TIME_MODIFIED)) // if f1 is newer than f2... { new_selection1.insert(f1); // ... add f1 to files to be selected in fl1... files2.erase(i2); // ... and remove f2 from fl2 files continue; } if (f1->GetGfileAttributeUInt64(G_FILE_ATTRIBUTE_TIME_MODIFIED) == f2->GetGfileAttributeUInt64(G_FILE_ATTRIBUTE_TIME_MODIFIED)) // if the same mtime for f1, f2 { if (f1->GetGfileAttributeUInt64(G_FILE_ATTRIBUTE_STANDARD_SIZE) == f2->GetGfileAttributeUInt64(G_FILE_ATTRIBUTE_STANDARD_SIZE)) // ... then check f1, f2 sizes files2.erase(i2); // ... and remove f2 from fl2 files else new_selection1.insert(f1); // ... or add f1 to files to be selected in fl1 } } transform (files2.begin(), files2.end(), inserter(new_selection2,new_selection2.begin()), make_select2nd(files2)); // copy left files2 --> new_selection2 selection_delta (*fl1, fl1->get_marked_files(), new_selection1); selection_delta (*fl2, fl2->get_marked_files(), new_selection2); } /* ***************************** View Menu ****************************** */ /* Changing of GSettings here will trigger functions in gnome-cmd-data.cc */ /* ********************************************************************** */ void view_conbuttons (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_SHOW_DEVBUTTONS, active); } void view_devlist (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_SHOW_DEVLIST, active); } void view_toolbar (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_SHOW_TOOLBAR, active); } void view_buttonbar (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_SHOW_BUTTONBAR, active); } void view_cmdline (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_SHOW_CMDLINE, active); } void view_dir_history (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_dir_indicator_show_history (GNOME_CMD_DIR_INDICATOR (get_fs (ACTIVE)->dir_indicator)); } void view_hidden_files (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->filter, GCMD_SETTINGS_FILTER_HIDE_HIDDEN, !active); } void view_backup_files (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->filter, GCMD_SETTINGS_FILTER_HIDE_BACKUPS, !active); } void view_horizontal_orientation (GtkToggleAction *toggleAction, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; auto active = gtk_toggle_action_get_active(toggleAction); g_settings_set_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_HORIZONTAL_ORIENTATION, active); } void view_step_up (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFileList *fl = fs->file_list(); g_signal_emit_by_name (fl, "scroll-vertical", GTK_SCROLL_STEP_BACKWARD, 0.0, nullptr); } void view_step_down (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFileList *fl = fs->file_list(); g_signal_emit_by_name (fl, "scroll-vertical", GTK_SCROLL_STEP_FORWARD, 0.0, nullptr); } void view_up (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFileList *fl = fs->file_list(); if (fl->locked) fs->new_tab(gnome_cmd_dir_get_parent (fl->cwd)); else fs->goto_directory(".."); } void view_main_menu (GtkMenuItem *menuitem, gpointer not_used) { if (!gtk_widget_get_realized ( GTK_WIDGET (main_win))) return; gboolean mainmenu_visibility; mainmenu_visibility = g_settings_get_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_MAINMENU_VISIBILITY); g_settings_set_boolean (gcmd_user_actions.settings->general, GCMD_SETTINGS_MAINMENU_VISIBILITY, !mainmenu_visibility); } void view_first (GtkMenuItem *menuitem, gpointer not_used) { get_fs (ACTIVE)->first(); } void view_back (GtkMenuItem *menuitem, gpointer not_used) { get_fs (ACTIVE)->back(); } void view_forward (GtkMenuItem *menuitem, gpointer not_used) { get_fs (ACTIVE)->forward(); } void view_last (GtkMenuItem *menuitem, gpointer not_used) { get_fs (ACTIVE)->last(); } void view_refresh (GtkMenuItem *menuitem, gpointer file_list) { GnomeCmdFileList *fl = GNOME_CMD_IS_FILE_LIST(file_list) ? GNOME_CMD_FILE_LIST (file_list) : get_fl (ACTIVE); fl->reload(); } void view_equal_panes (GtkMenuItem *menuitem, gpointer not_used) { main_win->set_equal_panes(); } void view_maximize_pane (GtkMenuItem *menuitem, gpointer not_used) { main_win->maximize_pane(); } void view_in_left_pane (GtkMenuItem *menuitem, gpointer not_used) { main_win->set_fs_directory_to_opposite(LEFT); } void view_in_right_pane (GtkMenuItem *menuitem, gpointer not_used) { main_win->set_fs_directory_to_opposite(RIGHT); } void view_in_active_pane (GtkMenuItem *menuitem, gpointer not_used) { main_win->set_fs_directory_to_opposite(ACTIVE); } void view_in_inactive_pane (GtkMenuItem *menuitem, gpointer not_used) { main_win->set_fs_directory_to_opposite(INACTIVE); } void view_directory (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFileList *fl = fs->file_list(); GnomeCmdFile *f = fl->get_selected_file(); if (f && f->GetGfileAttributeUInt32(G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_DIRECTORY) fs->do_file_specific_action (fl, f); } void view_home (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFileList *fl = fs->file_list(); if (!fl->locked) { fl->set_connection(get_home_con ()); fl->goto_directory("~"); } else fs->new_tab(gnome_cmd_dir_new (get_home_con (), gnome_cmd_con_create_path (get_home_con (), g_get_home_dir ()))); } void view_root (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFileList *fl = fs->file_list(); if (fl->locked) fs->new_tab(gnome_cmd_dir_new (fl->con, gnome_cmd_con_create_path (fl->con, "/"))); else fs->goto_directory("/"); } void view_new_tab (GtkMenuItem *menuitem, gpointer file_list) { GnomeCmdFileList *fl = file_list ? GNOME_CMD_FILE_LIST (file_list) : get_fl (ACTIVE); GnomeCmdFileSelector *fs = GNOME_CMD_FILE_SELECTOR (gtk_widget_get_ancestor (*fl, GNOME_CMD_TYPE_FILE_SELECTOR)); fs->new_tab(fl->cwd); } void view_close_tab (GtkMenuItem *menuitem, gpointer file_list) { if (file_list) { GnomeCmdFileList *fl = GNOME_CMD_FILE_LIST (file_list); GnomeCmdFileSelector *fs = GNOME_CMD_FILE_SELECTOR (gtk_widget_get_ancestor (*fl, GNOME_CMD_TYPE_FILE_SELECTOR)); if (fs->notebook->size()>1) if (!fl->locked || gnome_cmd_prompt_message (*main_win, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, _("The tab is locked, close anyway?"))==GTK_RESPONSE_OK) fs->close_tab(gtk_notebook_page_num (*fs->notebook, gtk_widget_get_parent (*fl))); } else { GnomeCmdFileSelector *fs = get_fs (ACTIVE); if (fs->notebook->size()>1) if (!fs->file_list()->locked || gnome_cmd_prompt_message (*main_win, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, _("The tab is locked, close anyway?"))==GTK_RESPONSE_OK) fs->close_tab(); } } void view_close_all_tabs (GtkMenuItem *menuitem, gpointer file_selector) { GnomeCmdFileSelector *fs = file_selector ? GNOME_CMD_FILE_SELECTOR (file_selector) : get_fs (ACTIVE); GnomeCmdNotebook *notebook = fs->notebook; gint n = notebook->get_current_page(); for (gint i=notebook->size(); i--;) if (i!=n && !fs->file_list(i)->locked) notebook->remove_page(i); } void view_close_duplicate_tabs (GtkMenuItem *menuitem, gpointer file_selector) { GnomeCmdFileSelector *fs = file_selector ? GNOME_CMD_FILE_SELECTOR (file_selector) : get_fs (ACTIVE); GnomeCmdNotebook *notebook = fs->notebook; typedef set TABS_COLL; typedef map DIRS_COLL; DIRS_COLL dirs; for (gint i=notebook->size(); i--;) { GnomeCmdFileList *fl = GNOME_CMD_FILE_LIST (gtk_bin_get_child (GTK_BIN (notebook->page(i)))); if (fl && !fl->locked) dirs[fl->cwd].insert(i); } TABS_COLL duplicates; DIRS_COLL::iterator pos = dirs.find(fs->get_directory()); // find tabs with the current dir... if (pos!=dirs.end()) { duplicates.swap(pos->second); // ... and mark them as to be closed... duplicates.erase(notebook->get_current_page()); // ... but WITHOUT the current one dirs.erase(pos); } for (DIRS_COLL::const_iterator i=dirs.begin(); i!=dirs.end(); ++i) { TABS_COLL::iterator beg = i->second.begin(); copy(++beg, i->second.end(), inserter(duplicates, duplicates.begin())); // for every dir, leave the first tab opened } for (TABS_COLL::const_reverse_iterator i=duplicates.rbegin(); i!=duplicates.rend(); ++i) notebook->remove_page(*i); } void view_prev_tab (GtkMenuItem *menuitem, gpointer not_used) { get_fs (ACTIVE)->notebook->prev_page(); } void view_next_tab (GtkMenuItem *menuitem, gpointer not_used) { get_fs (ACTIVE)->notebook->next_page(); } void view_in_new_tab (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdFileSelector *fs = get_fs (ACTIVE); GnomeCmdFile *file = fs->file_list()->get_selected_file(); if (file && file->GetGfileAttributeUInt32(G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_DIRECTORY) fs->new_tab(GNOME_CMD_DIR (file), FALSE); else fs->new_tab(fs->get_directory(), FALSE); } void view_in_inactive_tab (GtkMenuItem *menuitem, gpointer file_list) { GnomeCmdFileList *fl = file_list ? GNOME_CMD_FILE_LIST (file_list) : get_fl (ACTIVE); GnomeCmdFile *file = fl->get_selected_file(); if (file && file->GetGfileAttributeUInt32(G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_DIRECTORY) get_fs (INACTIVE)->new_tab(GNOME_CMD_DIR (file), FALSE); else get_fs (INACTIVE)->new_tab(fl->cwd, FALSE); } void view_toggle_tab_lock (GtkMenuItem *menuitem, gpointer page) { // 0 -> current tab // 1 .. n -> tab #n for active fs // -1 .. -n -> tab #n for inactive fs int n = GPOINTER_TO_INT (page); GnomeCmdFileSelector *fs = get_fs (n>=0 ? ACTIVE : INACTIVE); GnomeCmdFileList *fl = n==0 ? get_fl (ACTIVE) : fs->file_list(ABS(n)-1); if (fs && fl) { fl->locked = !fl->locked; fs->update_tab_label(fl); } } /************** Options Menu **************/ void options_edit (GtkMenuItem *menuitem, gpointer not_used) { if (gnome_cmd_options_dialog (*main_win, gnome_cmd_data.options)) { main_win->update_view(); gnome_cmd_data.save(); } } void options_edit_shortcuts (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_key_shortcuts_dialog_new (gcmd_user_actions); } /************** Connections Menu **************/ void connections_open (GtkMenuItem *menuitem, gpointer not_used) { GtkWidget *dialog = gnome_cmd_remote_dialog_new (); g_object_ref (dialog); gtk_widget_show (dialog); } void connections_new (GtkMenuItem *menuitem, gpointer not_used) { show_quick_connect_dialog (); } void connections_change (GtkMenuItem *menuitem, gpointer con) // this function is NOT exposed to user as UserAction { get_fl (ACTIVE)->set_connection(static_cast (con)); } void connections_change_left (GtkMenuItem *menuitem, gpointer con) { main_win->change_connection(LEFT); } void connections_change_right (GtkMenuItem *menuitem, gpointer con) { main_win->change_connection(RIGHT); } void connections_close (GtkMenuItem *menuitem, gpointer con) // this function is NOT exposed to user as UserAction { GnomeCmdFileSelector *active = get_fs (ACTIVE); GnomeCmdFileSelector *inactive = get_fs (INACTIVE); GnomeCmdCon *home = get_home_con (); if (con == active->get_connection()) active->set_connection(home); if (con == inactive->get_connection()) inactive->set_connection(home); gnome_cmd_con_close (static_cast (con)); } void connections_close_current (GtkMenuItem *menuitem, gpointer not_used) { GnomeCmdCon *con = get_fs (ACTIVE)->get_connection(); if (!GNOME_CMD_IS_CON_HOME (con)) connections_close (menuitem, con); } /************** Bookmarks Menu **************/ void bookmarks_add_current (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_bookmark_add_current (get_fs (ACTIVE)->get_directory()); } void bookmarks_edit (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_bookmark_dialog_new (_("Bookmarks"), *main_win); } void bookmarks_goto (GtkMenuItem *menuitem, gpointer bookmark_name) { if (!bookmark_name) return; vector a; guint n = split(static_cast(bookmark_name), a, "|"); string group = n<2 || a[0].empty() ? "local" : a[0]; string name = a[n<2 ? 0 : 1]; transform(group.begin(), group.end(), group.begin(), (int(*)(int))tolower); if (group=="local") { for (GList *bookmarks = gnome_cmd_con_get_bookmarks (get_home_con ())->bookmarks; bookmarks; bookmarks = bookmarks->next) { auto bookmark = static_cast (bookmarks->data); if (name == bookmark->name) { gnome_cmd_bookmark_goto (bookmark); return; } } g_warning ("[%s] Unknown bookmark name: '%s' - ignored", (char *) bookmark_name, name.c_str()); } #ifdef HAVE_SAMBA else if (group=="smb" || group=="samba") { for (GList *bookmarks = gnome_cmd_con_get_bookmarks (get_smb_con ())->bookmarks; bookmarks; bookmarks = bookmarks->next) { auto bookmark = static_cast (bookmarks->data); if (name == bookmark->name) { gnome_cmd_bookmark_goto (bookmark); return; } } g_warning ("[%s] Unknown bookmark name: '%s' - ignored", (char *) bookmark_name, name.c_str()); } #endif else g_warning ("[%s] Unsupported bookmark group: '%s' - ignored", (char *) bookmark_name, group.c_str()); } void bookmarks_view (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_dir_indicator_show_bookmarks (GNOME_CMD_DIR_INDICATOR (get_fs (ACTIVE)->dir_indicator)); } /************** Plugins Menu **************/ void plugins_configure (GtkMenuItem *menuitem, gpointer not_used) { plugin_manager_show (); } /************** Help Menu **************/ void help_help (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_help_display ("gnome-commander.xml"); } void help_keyboard (GtkMenuItem *menuitem, gpointer not_used) { gnome_cmd_help_display ("gnome-commander.xml", "gnome-commander-keyboard"); } void help_web (GtkMenuItem *menuitem, gpointer not_used) { GError *error = nullptr; if (!gtk_show_uri (nullptr, PACKAGE_URL, GDK_CURRENT_TIME, &error)) gnome_cmd_error_message (_("There was an error opening home page."), error); } void help_problem (GtkMenuItem *menuitem, gpointer not_used) { GError *error = nullptr; if (!gtk_show_uri (nullptr, PACKAGE_BUGREPORT, GDK_CURRENT_TIME, &error)) gnome_cmd_error_message (_("There was an error reporting problem."), error); } void help_about (GtkMenuItem *menuitem, gpointer not_used) { static const gchar *authors[] = { "Marcus Bjurman ", "Piotr Eljasiak ", "Assaf Gordon ", "Uwe Scholz ", nullptr }; static const gchar *documenters[] = { "Marcus Bjurman ", "Piotr Eljasiak ", "Laurent Coudeur ", "Uwe Scholz ", nullptr }; static const gchar copyright[] = "Copyright \xc2\xa9 2001-2006 Marcus Bjurman\n" "Copyright \xc2\xa9 2007-2012 Piotr Eljasiak\n" "Copyright \xc2\xa9 2013-2023 Uwe Scholz"; static const gchar comments[] = N_("A fast and powerful file manager for the GNOME desktop"); static const gchar *license[] = { N_("GNOME Commander 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."), N_("GNOME Commander 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."), N_("You should have received a copy of the GNU General Public License " "along with GNOME Commander; if not, write to the Free Software Foundation, Inc., " "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.") }; gchar *license_trans = g_strjoin ("\n\n", _(license[0]), _(license[1]), _(license[2]), nullptr); gtk_show_about_dialog (*main_win, "name", "GNOME Commander", "version", PACKAGE_VERSION, "comments", _(comments), "copyright", copyright, "license", license_trans, "wrap-license", TRUE, "authors", authors, "documenters", documenters, "logo-icon-name", PACKAGE_NAME, "translator-credits", _("translator-credits"), "website", "https://gcmd.github.io", "website-label", "GNOME Commander Website", nullptr); g_free (license_trans); } /** * Parses a command, stored in a char array, and substitutes a certain * symbol with path or URI names. The result is stored in a string * variable, which is expanded if needed. Possible symbols are: * \li \%f: file name (or list for multiple selections) * \li \%F: quoted filename (or list for multiple selections) * \li \%p: full file system path (or list for multiple selections) * \li \%P: quoted full file system path (or list for multiple selections) * \li \%s: synonym for %P (for compatibility with previous versions of gcmd) * \li \%u: fully qualified URI for the file (or list for multiple selections) * \li \%d: full path to the directory containing file * \li \%D: quoted full path to the directory containg file * \li \%%: percent sign * Trailing blanks are removed. * * \param[in] command A char array to be parsed * \param[out] cmd A string with parsed symbols listed above * \returns Length of the cmd string */ int parse_command(string *cmd, const gchar *command) { gboolean percent = FALSE; gboolean blcheck = FALSE; string filename; string quoted_filename; string file_path; string quoted_file_path; string dir_path; string quoted_dir_path; string uri; unsigned cmdcap; unsigned cmdlen; GnomeCmdFileList *fl = get_fl (ACTIVE); GList *sfl = fl->get_selected_files(); if (sfl) { auto gnomeCmdFile = (GnomeCmdFile*) sfl->data; auto gFileParent = g_file_get_parent(gnomeCmdFile->gFile); if (gnomeCmdFile->is_local()) { dir_path = g_file_get_path(gFileParent); } else { dir_path = g_file_get_uri(gFileParent); } g_object_unref(gFileParent); } else { // ToDo: Fix this misleading usage of the home directory dir_path = g_get_home_dir(); } cmdcap = cmd->capacity(); cmdlen = cmd->length(); for (auto s = command; *s; ++s) // loop over chars of command-string set by user { if (!percent) // check if % not found { percent = (*s == '%'); // true: if s=%, false: if s!=% if (!percent) // check if % not found { if (cmdcap < cmdlen + 1) { cmd->reserve(cmdlen +1); cmdcap = cmd->capacity(); } if (blcheck) // copy letter by letter, but remove trailing blanks *cmd += *s; else { if (*s == ' ' || *s == '\t') { continue; } else { blcheck = TRUE; *cmd += *s; } } cmdlen = cmd->length(); } continue; } switch (*s) { case 'f': // %f file name (or list for multiple selections) if (filename.empty()) get_file_list (filename, sfl, gnome_cmd_file_get_name); if (cmdcap < cmdlen + filename.length()) { cmd->reserve(cmdlen + filename.length()); cmdcap = cmd->capacity(); } *cmd += filename; cmdlen = cmd->length(); break; case 'F': // %F quoted filename (or list for multiple selections) if (quoted_filename.empty()) get_file_list (quoted_filename, sfl, gnome_cmd_file_get_quoted_name); if (cmdcap < cmdlen + quoted_filename.length()) { cmd->reserve(cmdlen + quoted_filename.length()); cmdcap = cmd->capacity(); } *cmd += quoted_filename; cmdlen = cmd->length(); break; case 'p': // %p full file system path (or list for multiple selections) if (file_path.empty()) get_file_list (file_path, sfl, gnome_cmd_file_get_real_path); if (cmdcap < cmdlen + file_path.length()) { cmd->reserve(cmdlen + file_path.length()); cmdcap = cmd->capacity(); } *cmd += file_path; cmdlen = cmd->length(); break; case 'P': // %P quoted full file system path (or list for multiple selections) case 's': // %s synonym for %P (for compatibility with previous versions of gcmd) if (quoted_file_path.empty()) get_file_list (quoted_file_path, sfl, gnome_cmd_file_get_quoted_real_path); if (cmdcap < cmdlen + quoted_file_path.length()) { cmd->reserve(cmdlen + quoted_file_path.length()); cmdcap = cmd->capacity(); } *cmd += quoted_file_path; cmdlen = cmd->length(); break; case 'u': // %u fully qualified URI for the file (or list for multiple selections) if (uri.empty()) get_file_list (uri, sfl, gnome_cmd_file_get_uri_str); if (cmdcap < cmdlen + uri.length()) { cmd->reserve(cmdlen + uri.length()); cmdcap = cmd->capacity(); } *cmd += uri; cmdlen = cmd->length(); break; case 'd': // %d full path to the directory containing file if (cmdcap < cmdlen + dir_path.length()) { cmd->reserve(cmdlen + dir_path.length()); cmdcap = cmd->capacity(); } *cmd += dir_path; cmdlen = cmd->length(); break; case 'D': // %D quoted full path to the directory containg file if (cmdcap < cmdlen + quoted_dir_path.length()) { cmd->reserve(cmdlen + quoted_dir_path.length()); cmdcap = cmd->capacity(); } *cmd += quoted_dir_path; cmdlen = cmd->length(); break; default: if (cmdcap < cmdlen + 1) { cmd->reserve(cmdlen +1); cmdcap = cmd->capacity(); } *cmd += '%'; cmdlen = cmd->length(); #if defined (__GNUC__) && __GNUC__ >= 7 __attribute__ ((fallthrough)); #endif case '%': // %% percent sign if (cmdcap < cmdlen + 1) { cmd->reserve(cmdlen +1); cmdcap = cmd->capacity(); } *cmd += *s; cmdlen = cmd->length(); break; } percent = FALSE; } if (percent) *cmd += '%'; return cmd->length(); }