/* Editor macros engine Copyright (C) 2001-2022 Free Software Foundation, Inc. This file is part of the Midnight Commander. The Midnight 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 3 of the License, or (at your option) any later version. The Midnight 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. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "lib/global.h" #include "lib/mcconfig.h" #include "lib/tty/key.h" /* lookup_key*() */ #include "lib/keybind.h" /* keybind_lookup_actionname() */ #include "lib/fileloc.h" #include "src/setup.h" /* macro_action_t */ #include "src/history.h" /* MC_HISTORY_EDIT_REPEAT */ #include "editwidget.h" #include "editmacros.h" /*** global variables ****************************************************************************/ /*** file scope macro definitions ****************************************************************/ /*** file scope type declarations ****************************************************************/ /*** file scope variables ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ static int edit_macro_comparator (gconstpointer * macro1, gconstpointer * macro2) { const macros_t *m1 = (const macros_t *) macro1; const macros_t *m2 = (const macros_t *) macro2; return m1->hotkey - m2->hotkey; } /* --------------------------------------------------------------------------------------------- */ static void edit_macro_sort_by_hotkey (void) { if (macros_list != NULL && macros_list->len != 0) g_array_sort (macros_list, (GCompareFunc) edit_macro_comparator); } /* --------------------------------------------------------------------------------------------- */ static int edit_get_macro (WEdit * edit, int hotkey) { macros_t *array_start; macros_t *result; macros_t search_macro = { .hotkey = hotkey }; (void) edit; result = bsearch (&search_macro, macros_list->data, macros_list->len, sizeof (macros_t), (GCompareFunc) edit_macro_comparator); if (result == NULL || result->macro == NULL) return (-1); array_start = &g_array_index (macros_list, struct macros_t, 0); return (int) (result - array_start); } /* --------------------------------------------------------------------------------------------- */ /** returns FALSE on error */ static gboolean edit_delete_macro (WEdit * edit, int hotkey) { mc_config_t *macros_config = NULL; const char *section_name = "editor"; gchar *macros_fname; int indx; char *skeyname; /* clear array of actions for current hotkey */ while ((indx = edit_get_macro (edit, hotkey)) != -1) { macros_t *macros; macros = &g_array_index (macros_list, struct macros_t, indx); g_array_free (macros->macro, TRUE); g_array_remove_index (macros_list, indx); } macros_fname = mc_config_get_full_path (MC_MACRO_FILE); macros_config = mc_config_init (macros_fname, FALSE); g_free (macros_fname); if (macros_config == NULL) return FALSE; skeyname = lookup_key_by_code (hotkey); while (mc_config_del_key (macros_config, section_name, skeyname)) ; g_free (skeyname); mc_config_save_file (macros_config, NULL); mc_config_deinit (macros_config); return TRUE; } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ /** returns FALSE on error */ gboolean edit_store_macro_cmd (WEdit * edit) { int i; int hotkey; GString *macros_string = NULL; const char *section_name = "editor"; gchar *macros_fname; GArray *macros = NULL; int tmp_act; mc_config_t *macros_config; char *skeyname; hotkey = editcmd_dialog_raw_key_query (_("Save macro"), _("Press the macro's new hotkey:"), TRUE); if (hotkey == ESC_CHAR) return FALSE; tmp_act = keybind_lookup_keymap_command (WIDGET (edit)->keymap, hotkey); /* return FALSE if try assign macro into restricted hotkeys */ if (tmp_act == CK_MacroStartRecord || tmp_act == CK_MacroStopRecord || tmp_act == CK_MacroStartStopRecord) return FALSE; edit_delete_macro (edit, hotkey); macros_fname = mc_config_get_full_path (MC_MACRO_FILE); macros_config = mc_config_init (macros_fname, FALSE); g_free (macros_fname); if (macros_config == NULL) return FALSE; edit_push_undo_action (edit, KEY_PRESS + edit->start_display); skeyname = lookup_key_by_code (hotkey); for (i = 0; i < macro_index; i++) { macro_action_t m_act; const char *action_name; action_name = keybind_lookup_actionname (record_macro_buf[i].action); if (action_name == NULL) break; if (macros == NULL) { macros = g_array_new (TRUE, FALSE, sizeof (macro_action_t)); macros_string = g_string_sized_new (250); } m_act.action = record_macro_buf[i].action; m_act.ch = record_macro_buf[i].ch; g_array_append_val (macros, m_act); g_string_append_printf (macros_string, "%s:%i;", action_name, (int) record_macro_buf[i].ch); } if (macros == NULL) mc_config_del_key (macros_config, section_name, skeyname); else { macros_t macro; macro.hotkey = hotkey; macro.macro = macros; g_array_append_val (macros_list, macro); mc_config_set_string (macros_config, section_name, skeyname, macros_string->str); } g_free (skeyname); edit_macro_sort_by_hotkey (); if (macros_string != NULL) g_string_free (macros_string, TRUE); mc_config_save_file (macros_config, NULL); mc_config_deinit (macros_config); return TRUE; } /* --------------------------------------------------------------------------------------------- */ /** return FALSE on error */ gboolean edit_load_macro_cmd (WEdit * edit) { mc_config_t *macros_config = NULL; gchar **profile_keys, **keys; gchar **values, **curr_values; const char *section_name = "editor"; gchar *macros_fname; (void) edit; if (macros_list == NULL || macros_list->len != 0) return FALSE; macros_fname = mc_config_get_full_path (MC_MACRO_FILE); macros_config = mc_config_init (macros_fname, TRUE); g_free (macros_fname); if (macros_config == NULL) return FALSE; keys = mc_config_get_keys (macros_config, section_name, NULL); for (profile_keys = keys; *profile_keys != NULL; profile_keys++) { int hotkey; GArray *macros = NULL; macros_t macro; values = mc_config_get_string_list (macros_config, section_name, *profile_keys, NULL); hotkey = lookup_key (*profile_keys, NULL); for (curr_values = values; *curr_values != NULL && *curr_values[0] != '\0'; curr_values++) { char **macro_pair; macro_pair = g_strsplit (*curr_values, ":", 2); if (macro_pair != NULL) { macro_action_t m_act = { .action = 0, .ch = -1 }; if (macro_pair[0] != NULL && macro_pair[0][0] != '\0') m_act.action = keybind_lookup_action (macro_pair[0]); if (macro_pair[1] != NULL && macro_pair[1][0] != '\0') m_act.ch = strtol (macro_pair[1], NULL, 0); if (m_act.action != 0) { /* a shell command */ if ((m_act.action / CK_PipeBlock (0)) == 1) { m_act.action = CK_PipeBlock (0); if (m_act.ch > 0) m_act.action += m_act.ch; m_act.ch = -1; } if (macros == NULL) macros = g_array_new (TRUE, FALSE, sizeof (m_act)); g_array_append_val (macros, m_act); } g_strfreev (macro_pair); } } if (macros != NULL) { macro.hotkey = hotkey; macro.macro = macros; g_array_append_val (macros_list, macro); } g_strfreev (values); } g_strfreev (keys); mc_config_deinit (macros_config); edit_macro_sort_by_hotkey (); return TRUE; } /* --------------------------------------------------------------------------------------------- */ void edit_delete_macro_cmd (WEdit * edit) { int hotkey; hotkey = editcmd_dialog_raw_key_query (_("Delete macro"), _("Press macro hotkey:"), TRUE); if (hotkey != 0 && !edit_delete_macro (edit, hotkey)) message (D_ERROR, _("Delete macro"), _("Macro not deleted")); } /* --------------------------------------------------------------------------------------------- */ gboolean edit_repeat_macro_cmd (WEdit * edit) { gboolean ok; char *f; long count_repeat = 0; f = input_dialog (_("Repeat last commands"), _("Repeat times:"), MC_HISTORY_EDIT_REPEAT, NULL, INPUT_COMPLETE_NONE); ok = (f != NULL && *f != '\0'); if (ok) { char *error = NULL; count_repeat = strtol (f, &error, 0); ok = (*error == '\0'); } g_free (f); if (ok) { int i, j; edit_push_undo_action (edit, KEY_PRESS + edit->start_display); edit->force |= REDRAW_PAGE; for (j = 0; j < count_repeat; j++) for (i = 0; i < macro_index; i++) edit_execute_cmd (edit, record_macro_buf[i].action, record_macro_buf[i].ch); edit_update_screen (edit); } return ok; } /* --------------------------------------------------------------------------------------------- */ /** returns FALSE on error */ gboolean edit_execute_macro (WEdit * edit, int hotkey) { gboolean res = FALSE; if (hotkey != 0) { int indx; indx = edit_get_macro (edit, hotkey); if (indx != -1) { const macros_t *macros; macros = &g_array_index (macros_list, struct macros_t, indx); if (macros->macro->len != 0) { guint i; edit->force |= REDRAW_PAGE; for (i = 0; i < macros->macro->len; i++) { const macro_action_t *m_act; m_act = &g_array_index (macros->macro, struct macro_action_t, i); edit_execute_cmd (edit, m_act->action, m_act->ch); res = TRUE; } } } } return res; } /* --------------------------------------------------------------------------------------------- */ void edit_begin_end_macro_cmd (WEdit * edit) { /* edit is a pointer to the widget */ if (edit != NULL) { long command = macro_index < 0 ? CK_MacroStartRecord : CK_MacroStopRecord; edit_execute_key_command (edit, command, -1); } } /* --------------------------------------------------------------------------------------------- */ void edit_begin_end_repeat_cmd (WEdit * edit) { /* edit is a pointer to the widget */ if (edit != NULL) { long command = macro_index < 0 ? CK_RepeatStartRecord : CK_RepeatStopRecord; edit_execute_key_command (edit, command, -1); } } /* --------------------------------------------------------------------------------------------- */