root/src/editor/editmacros.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. edit_macro_comparator
  2. edit_macro_sort_by_hotkey
  3. edit_get_macro
  4. edit_delete_macro
  5. edit_store_macro_cmd
  6. edit_load_macro_cmd
  7. edit_delete_macro_cmd
  8. edit_repeat_macro_cmd
  9. edit_execute_macro
  10. edit_begin_end_macro_cmd
  11. edit_begin_end_repeat_cmd

   1 /*
   2    Editor macros engine
   3 
   4    Copyright (C) 2001-2024
   5    Free Software Foundation, Inc.
   6 
   7    This file is part of the Midnight Commander.
   8 
   9    The Midnight Commander is free software: you can redistribute it
  10    and/or modify it under the terms of the GNU General Public License as
  11    published by the Free Software Foundation, either version 3 of the License,
  12    or (at your option) any later version.
  13 
  14    The Midnight Commander is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18 
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21  */
  22 
  23 #include <config.h>
  24 
  25 #include <stdlib.h>
  26 
  27 #include "lib/global.h"
  28 #include "lib/mcconfig.h"
  29 #include "lib/tty/key.h"        /* tty_keyname_to_keycode*() */
  30 #include "lib/keybind.h"        /* keybind_lookup_actionname() */
  31 #include "lib/fileloc.h"
  32 
  33 #include "src/setup.h"          /* macro_action_t */
  34 #include "src/history.h"        /* MC_HISTORY_EDIT_REPEAT */
  35 
  36 #include "editwidget.h"
  37 
  38 #include "editmacros.h"
  39 
  40 /*** global variables ****************************************************************************/
  41 
  42 /*** file scope macro definitions ****************************************************************/
  43 
  44 /*** file scope type declarations ****************************************************************/
  45 
  46 /*** forward declarations (file scope functions) *************************************************/
  47 
  48 /*** file scope variables ************************************************************************/
  49 
  50 /* --------------------------------------------------------------------------------------------- */
  51 /*** file scope functions ************************************************************************/
  52 /* --------------------------------------------------------------------------------------------- */
  53 
  54 static int
  55 edit_macro_comparator (gconstpointer * macro1, gconstpointer * macro2)
     /* [previous][next][first][last][top][bottom][index][help]  */
  56 {
  57     const macros_t *m1 = (const macros_t *) macro1;
  58     const macros_t *m2 = (const macros_t *) macro2;
  59 
  60     return m1->hotkey - m2->hotkey;
  61 }
  62 
  63 /* --------------------------------------------------------------------------------------------- */
  64 
  65 static void
  66 edit_macro_sort_by_hotkey (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  67 {
  68     if (macros_list != NULL && macros_list->len != 0)
  69         g_array_sort (macros_list, (GCompareFunc) edit_macro_comparator);
  70 }
  71 
  72 /* --------------------------------------------------------------------------------------------- */
  73 
  74 static int
  75 edit_get_macro (WEdit * edit, int hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
  76 {
  77     macros_t *array_start;
  78     macros_t *result;
  79     macros_t search_macro = {
  80         .hotkey = hotkey
  81     };
  82 
  83     (void) edit;
  84 
  85     result = bsearch (&search_macro, macros_list->data, macros_list->len,
  86                       sizeof (macros_t), (GCompareFunc) edit_macro_comparator);
  87 
  88     if (result == NULL || result->macro == NULL)
  89         return (-1);
  90 
  91     array_start = &g_array_index (macros_list, struct macros_t, 0);
  92 
  93     return (int) (result - array_start);
  94 }
  95 
  96 /* --------------------------------------------------------------------------------------------- */
  97 
  98 /** returns FALSE on error */
  99 static gboolean
 100 edit_delete_macro (WEdit * edit, int hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 101 {
 102     mc_config_t *macros_config = NULL;
 103     const char *section_name = "editor";
 104     gchar *macros_fname;
 105     int indx;
 106     char *skeyname;
 107 
 108     /* clear array of actions for current hotkey */
 109     while ((indx = edit_get_macro (edit, hotkey)) != -1)
 110     {
 111         macros_t *macros;
 112 
 113         macros = &g_array_index (macros_list, struct macros_t, indx);
 114         g_array_free (macros->macro, TRUE);
 115         g_array_remove_index (macros_list, indx);
 116     }
 117 
 118     macros_fname = mc_config_get_full_path (MC_MACRO_FILE);
 119     macros_config = mc_config_init (macros_fname, FALSE);
 120     g_free (macros_fname);
 121 
 122     if (macros_config == NULL)
 123         return FALSE;
 124 
 125     skeyname = tty_keycode_to_keyname (hotkey);
 126     while (mc_config_del_key (macros_config, section_name, skeyname))
 127         ;
 128     g_free (skeyname);
 129     mc_config_save_file (macros_config, NULL);
 130     mc_config_deinit (macros_config);
 131     return TRUE;
 132 }
 133 
 134 /* --------------------------------------------------------------------------------------------- */
 135 /*** public functions ****************************************************************************/
 136 /* --------------------------------------------------------------------------------------------- */
 137 
 138 /** returns FALSE on error */
 139 gboolean
 140 edit_store_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 141 {
 142     int i;
 143     int hotkey;
 144     GString *macros_string = NULL;
 145     const char *section_name = "editor";
 146     gchar *macros_fname;
 147     GArray *macros = NULL;
 148     int tmp_act;
 149     mc_config_t *macros_config;
 150     char *skeyname;
 151 
 152     hotkey =
 153         editcmd_dialog_raw_key_query (_("Save macro"), _("Press the macro's new hotkey:"), TRUE);
 154     if (hotkey == ESC_CHAR)
 155         return FALSE;
 156 
 157     tmp_act = keybind_lookup_keymap_command (WIDGET (edit)->keymap, hotkey);
 158     /* return FALSE if try assign macro into restricted hotkeys */
 159     if (tmp_act == CK_MacroStartRecord
 160         || tmp_act == CK_MacroStopRecord || tmp_act == CK_MacroStartStopRecord)
 161         return FALSE;
 162 
 163     edit_delete_macro (edit, hotkey);
 164 
 165     macros_fname = mc_config_get_full_path (MC_MACRO_FILE);
 166     macros_config = mc_config_init (macros_fname, FALSE);
 167     g_free (macros_fname);
 168 
 169     if (macros_config == NULL)
 170         return FALSE;
 171 
 172     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 173 
 174     skeyname = tty_keycode_to_keyname (hotkey);
 175 
 176     for (i = 0; i < macro_index; i++)
 177     {
 178         macro_action_t m_act;
 179         const char *action_name;
 180 
 181         action_name = keybind_lookup_actionname (record_macro_buf[i].action);
 182         if (action_name == NULL)
 183             break;
 184 
 185         if (macros == NULL)
 186         {
 187             macros = g_array_new (TRUE, FALSE, sizeof (macro_action_t));
 188             macros_string = g_string_sized_new (250);
 189         }
 190 
 191         m_act.action = record_macro_buf[i].action;
 192         m_act.ch = record_macro_buf[i].ch;
 193         g_array_append_val (macros, m_act);
 194         g_string_append_printf (macros_string, "%s:%i;", action_name, (int) record_macro_buf[i].ch);
 195     }
 196 
 197     if (macros == NULL)
 198         mc_config_del_key (macros_config, section_name, skeyname);
 199     else
 200     {
 201         macros_t macro;
 202 
 203         macro.hotkey = hotkey;
 204         macro.macro = macros;
 205         g_array_append_val (macros_list, macro);
 206         mc_config_set_string (macros_config, section_name, skeyname, macros_string->str);
 207     }
 208 
 209     g_free (skeyname);
 210 
 211     edit_macro_sort_by_hotkey ();
 212 
 213     if (macros_string != NULL)
 214         g_string_free (macros_string, TRUE);
 215     mc_config_save_file (macros_config, NULL);
 216     mc_config_deinit (macros_config);
 217 
 218     return TRUE;
 219 }
 220 
 221 /* --------------------------------------------------------------------------------------------- */
 222 
 223 /** return FALSE on error */
 224 
 225 gboolean
 226 edit_load_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 227 {
 228     mc_config_t *macros_config = NULL;
 229     gchar **profile_keys, **keys;
 230     gchar **values, **curr_values;
 231     const char *section_name = "editor";
 232     gchar *macros_fname;
 233 
 234     (void) edit;
 235 
 236     if (macros_list == NULL || macros_list->len != 0)
 237         return FALSE;
 238 
 239     macros_fname = mc_config_get_full_path (MC_MACRO_FILE);
 240     macros_config = mc_config_init (macros_fname, TRUE);
 241     g_free (macros_fname);
 242 
 243     if (macros_config == NULL)
 244         return FALSE;
 245 
 246     keys = mc_config_get_keys (macros_config, section_name, NULL);
 247 
 248     for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
 249     {
 250         int hotkey;
 251         GArray *macros = NULL;
 252 
 253         values = mc_config_get_string_list (macros_config, section_name, *profile_keys, NULL);
 254         hotkey = tty_keyname_to_keycode (*profile_keys, NULL);
 255 
 256         for (curr_values = values; *curr_values != NULL && *curr_values[0] != '\0'; curr_values++)
 257         {
 258             char **macro_pair;
 259 
 260             macro_pair = g_strsplit (*curr_values, ":", 2);
 261 
 262             if (macro_pair != NULL)
 263             {
 264                 macro_action_t m_act = {
 265                     .action = 0,
 266                     .ch = -1
 267                 };
 268 
 269                 if (macro_pair[0] != NULL && macro_pair[0][0] != '\0')
 270                     m_act.action = keybind_lookup_action (macro_pair[0]);
 271 
 272                 if (macro_pair[1] != NULL && macro_pair[1][0] != '\0')
 273                     m_act.ch = strtol (macro_pair[1], NULL, 0);
 274 
 275                 if (m_act.action != 0)
 276                 {
 277                     /* a shell command */
 278                     if ((m_act.action / CK_PipeBlock (0)) == 1)
 279                     {
 280                         m_act.action = CK_PipeBlock (0);
 281                         if (m_act.ch > 0)
 282                             m_act.action += m_act.ch;
 283                         m_act.ch = -1;
 284                     }
 285 
 286                     if (macros == NULL)
 287                         macros = g_array_new (TRUE, FALSE, sizeof (m_act));
 288 
 289                     g_array_append_val (macros, m_act);
 290                 }
 291 
 292                 g_strfreev (macro_pair);
 293             }
 294         }
 295 
 296         if (macros != NULL)
 297         {
 298             macros_t macro = {
 299                 .hotkey = hotkey,
 300                 .macro = macros
 301             };
 302 
 303             g_array_append_val (macros_list, macro);
 304         }
 305 
 306         g_strfreev (values);
 307     }
 308 
 309     g_strfreev (keys);
 310     mc_config_deinit (macros_config);
 311     edit_macro_sort_by_hotkey ();
 312 
 313     return TRUE;
 314 }
 315 
 316 /* --------------------------------------------------------------------------------------------- */
 317 
 318 void
 319 edit_delete_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 320 {
 321     int hotkey;
 322 
 323     hotkey = editcmd_dialog_raw_key_query (_("Delete macro"), _("Press macro hotkey:"), TRUE);
 324 
 325     if (hotkey != 0 && !edit_delete_macro (edit, hotkey))
 326         message (D_ERROR, _("Delete macro"), _("Macro not deleted"));
 327 }
 328 
 329 /* --------------------------------------------------------------------------------------------- */
 330 
 331 gboolean
 332 edit_repeat_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 333 {
 334     gboolean ok;
 335     char *f;
 336     long count_repeat = 0;
 337 
 338     f = input_dialog (_("Repeat last commands"), _("Repeat times:"), MC_HISTORY_EDIT_REPEAT, NULL,
 339                       INPUT_COMPLETE_NONE);
 340     ok = (f != NULL && *f != '\0');
 341 
 342     if (ok)
 343     {
 344         char *error = NULL;
 345 
 346         count_repeat = strtol (f, &error, 0);
 347 
 348         ok = (*error == '\0');
 349     }
 350 
 351     g_free (f);
 352 
 353     if (ok)
 354     {
 355         int i, j;
 356 
 357         edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
 358         edit->force |= REDRAW_PAGE;
 359 
 360         for (j = 0; j < count_repeat; j++)
 361             for (i = 0; i < macro_index; i++)
 362                 edit_execute_cmd (edit, record_macro_buf[i].action, record_macro_buf[i].ch);
 363 
 364         edit_update_screen (edit);
 365     }
 366 
 367     return ok;
 368 }
 369 
 370 /* --------------------------------------------------------------------------------------------- */
 371 
 372 /** returns FALSE on error */
 373 gboolean
 374 edit_execute_macro (WEdit * edit, int hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 375 {
 376     gboolean res = FALSE;
 377 
 378     if (hotkey != 0)
 379     {
 380         int indx;
 381 
 382         indx = edit_get_macro (edit, hotkey);
 383         if (indx != -1)
 384         {
 385             const macros_t *macros;
 386 
 387             macros = &g_array_index (macros_list, struct macros_t, indx);
 388             if (macros->macro->len != 0)
 389             {
 390                 guint i;
 391 
 392                 edit->force |= REDRAW_PAGE;
 393 
 394                 for (i = 0; i < macros->macro->len; i++)
 395                 {
 396                     const macro_action_t *m_act;
 397 
 398                     m_act = &g_array_index (macros->macro, struct macro_action_t, i);
 399                     edit_execute_cmd (edit, m_act->action, m_act->ch);
 400                     res = TRUE;
 401                 }
 402             }
 403         }
 404     }
 405 
 406     return res;
 407 }
 408 
 409 /* --------------------------------------------------------------------------------------------- */
 410 
 411 void
 412 edit_begin_end_macro_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 413 {
 414     /* edit is a pointer to the widget */
 415     if (edit != NULL)
 416     {
 417         long command = macro_index < 0 ? CK_MacroStartRecord : CK_MacroStopRecord;
 418 
 419         edit_execute_key_command (edit, command, -1);
 420     }
 421 }
 422 
 423  /* --------------------------------------------------------------------------------------------- */
 424 
 425 void
 426 edit_begin_end_repeat_cmd (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 427 {
 428     /* edit is a pointer to the widget */
 429     if (edit != NULL)
 430     {
 431         long command = macro_index < 0 ? CK_RepeatStartRecord : CK_RepeatStopRecord;
 432 
 433         edit_execute_key_command (edit, command, -1);
 434     }
 435 }
 436 
 437 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */