Manual pages: mcmcdiffmceditmcview

root/src/learn.c

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

DEFINITIONS

This source file includes following definitions.
  1. learn_button
  2. learn_move
  3. learn_check_key
  4. learn_callback
  5. init_learn
  6. learn_done
  7. learn_save
  8. learn_keys

   1 /*
   2    Learn keys
   3 
   4    Copyright (C) 1995-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Jakub Jelinek, 1995
   9    Andrew Borodin <aborodin@vmail.ru>, 2012, 2013
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  25  */
  26 
  27 /** \file learn.c
  28  *  \brief Source: learn keys module
  29  */
  30 
  31 #include <config.h>
  32 
  33 #include <stdlib.h>
  34 
  35 #include "lib/global.h"
  36 
  37 #include "lib/tty/tty.h"
  38 #include "lib/tty/key.h"
  39 #include "lib/mcconfig.h"
  40 #include "lib/strutil.h"
  41 #include "lib/terminal.h"  // convert_controls()
  42 #include "lib/util.h"      // MC_PTR_FREE
  43 #include "lib/widget.h"
  44 
  45 #include "setup.h"
  46 #include "learn.h"
  47 
  48 /*** global variables ****************************************************************************/
  49 
  50 /*** file scope macro definitions ****************************************************************/
  51 
  52 #define UX       4
  53 #define UY       2
  54 
  55 #define ROWS     13
  56 #define COLSHIFT 23
  57 
  58 /*** file scope type declarations ****************************************************************/
  59 
  60 typedef struct
  61 {
  62     Widget *button;
  63     Widget *label;
  64     gboolean ok;
  65     char *sequence;
  66 } learnkey_t;
  67 
  68 /*** forward declarations (file scope functions) *************************************************/
  69 
  70 /*** file scope variables ************************************************************************/
  71 
  72 static WDialog *learn_dlg;
  73 static const char *learn_title = N_ ("Learn keys");
  74 
  75 static learnkey_t *learnkeys = NULL;
  76 static int learn_total;
  77 static int learnok;
  78 static gboolean learnchanged = FALSE;
  79 
  80 /* --------------------------------------------------------------------------------------------- */
  81 /*** file scope functions ************************************************************************/
  82 /* --------------------------------------------------------------------------------------------- */
  83 
  84 static int
  85 learn_button (WButton *button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
  86 {
  87     WDialog *d;
  88     char *seq;
  89 
  90     (void) button;
  91 
  92     d = create_message (D_ERROR, _ ("Teach me a key"),
  93                         _ ("Please press the %s\n"
  94                            "and then wait until this message disappears.\n\n"
  95                            "Then, press it again to see if OK appears\n"
  96                            "next to its button.\n\n"
  97                            "If you want to escape, press a single Escape key\n"
  98                            "and wait as well."),
  99                         _ (key_name_conv_tab[action - B_USER].longname));
 100     mc_refresh ();
 101     if (learnkeys[action - B_USER].sequence != NULL)
 102         MC_PTR_FREE (learnkeys[action - B_USER].sequence);
 103 
 104     seq = learn_key ();
 105     if (seq != NULL)
 106     {
 107         /* Esc hides the dialog and do not allow definitions of
 108          * regular characters
 109          */
 110         gboolean seq_ok = FALSE;
 111 
 112         if (strcmp (seq, "\\e") != 0 && strcmp (seq, "\\e\\e") != 0 && strcmp (seq, "^m") != 0
 113             && strcmp (seq, "^i") != 0 && (seq[1] != '\0' || *seq < ' ' || *seq > '~'))
 114         {
 115             learnchanged = TRUE;
 116             learnkeys[action - B_USER].sequence = seq;
 117             seq = convert_controls (seq);
 118             seq_ok = define_sequence (key_name_conv_tab[action - B_USER].code, seq, MCKEY_NOACTION);
 119         }
 120 
 121         if (!seq_ok)
 122             message (D_NORMAL, _ ("Warning"),
 123                      _ ("Cannot accept this key.\nYou have entered \"%s\""), seq);
 124 
 125         g_free (seq);
 126     }
 127 
 128     dlg_run_done (d);
 129     widget_destroy (WIDGET (d));
 130 
 131     widget_select (learnkeys[action - B_USER].button);
 132 
 133     return 0;  // Do not kill learn_dlg
 134 }
 135 
 136 /* --------------------------------------------------------------------------------------------- */
 137 
 138 static gboolean
 139 learn_move (gboolean right)
     /* [previous][next][first][last][top][bottom][index][help]  */
 140 {
 141     int i, totalcols;
 142 
 143     totalcols = (learn_total - 1) / ROWS + 1;
 144     for (i = 0; i < learn_total; i++)
 145         if (learnkeys[i].button == WIDGET (GROUP (learn_dlg)->current->data))
 146         {
 147             if (right)
 148             {
 149                 if (i < learn_total - ROWS)
 150                     i += ROWS;
 151                 else
 152                     i %= ROWS;
 153             }
 154             else
 155             {
 156                 if (i / ROWS != 0)
 157                     i -= ROWS;
 158                 else if (i + (totalcols - 1) * ROWS >= learn_total)
 159                     i += (totalcols - 2) * ROWS;
 160                 else
 161                     i += (totalcols - 1) * ROWS;
 162             }
 163             widget_select (learnkeys[i].button);
 164             return TRUE;
 165         }
 166 
 167     return FALSE;
 168 }
 169 
 170 /* --------------------------------------------------------------------------------------------- */
 171 
 172 static gboolean
 173 learn_check_key (int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 174 {
 175     int i;
 176 
 177     for (i = 0; i < learn_total; i++)
 178     {
 179         if (key_name_conv_tab[i].code != c || learnkeys[i].ok)
 180             continue;
 181 
 182         widget_select (learnkeys[i].button);
 183         // TRANSLATORS: This label appears near learned keys.  Keep it short.
 184         label_set_text (LABEL (learnkeys[i].label), _ ("OK"));
 185         learnkeys[i].ok = TRUE;
 186         learnok++;
 187         if (learnok >= learn_total)
 188         {
 189             learn_dlg->ret_value = B_CANCEL;
 190             if (learnchanged)
 191             {
 192                 if (query_dialog (learn_title,
 193                                   _ ("It seems that all your keys already\n"
 194                                      "work fine. That's great."),
 195                                   D_ERROR, 2, _ ("&Save"), _ ("&Discard"))
 196                     == 0)
 197                     learn_dlg->ret_value = B_ENTER;
 198             }
 199             else
 200             {
 201                 message (D_ERROR, learn_title, "%s",
 202                          _ ("Great! You have a complete terminal database!\n"
 203                             "All your keys work well."));
 204             }
 205             dlg_close (learn_dlg);
 206         }
 207         return TRUE;
 208     }
 209 
 210     switch (c)
 211     {
 212     case KEY_LEFT:
 213     case 'h':
 214         return learn_move (FALSE);
 215     case KEY_RIGHT:
 216     case 'l':
 217         return learn_move (TRUE);
 218     case 'j':
 219         group_select_next_widget (GROUP (learn_dlg));
 220         return TRUE;
 221     case 'k':
 222         group_select_prev_widget (GROUP (learn_dlg));
 223         return TRUE;
 224     default:
 225         break;
 226     }
 227 
 228     /* Prevent from disappearing if a non-defined sequence is pressed
 229        and contains a button hotkey.  Only recognize hotkeys with ALT.  */
 230     return (c < 255 && g_ascii_isalnum (c));
 231 }
 232 
 233 /* --------------------------------------------------------------------------------------------- */
 234 
 235 static cb_ret_t
 236 learn_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 237 {
 238     switch (msg)
 239     {
 240     case MSG_KEY:
 241         return learn_check_key (parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
 242 
 243     default:
 244         return dlg_default_callback (w, sender, msg, parm, data);
 245     }
 246 }
 247 
 248 /* --------------------------------------------------------------------------------------------- */
 249 
 250 static void
 251 init_learn (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 252 {
 253     WGroup *g;
 254 
 255     const int dlg_width = 78;
 256     const int dlg_height = 23;
 257 
 258     // buttons
 259     WButton *bt0, *bt1;
 260     const char *b0 = _ ("&Save");
 261     const char *b1 = _ ("&Cancel");
 262 
 263     int x, y, i;
 264     const key_code_name_t *key;
 265 
 266 #ifdef ENABLE_NLS
 267     static gboolean i18n_flag = FALSE;
 268     if (!i18n_flag)
 269     {
 270         learn_title = _ (learn_title);
 271         i18n_flag = TRUE;
 272     }
 273 #endif
 274 
 275     do_refresh ();
 276 
 277     learn_dlg = dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors,
 278                             learn_callback, NULL, "[Learn keys]", learn_title);
 279     g = GROUP (learn_dlg);
 280 
 281     // find first unshown button
 282     for (key = key_name_conv_tab, learn_total = 0;
 283          key->name != NULL && strcmp (key->name, "kpleft") != 0; key++, learn_total++)
 284         ;
 285 
 286     learnok = 0;
 287     learnchanged = FALSE;
 288 
 289     learnkeys = g_new (learnkey_t, learn_total);
 290 
 291     x = UX;
 292     y = UY;
 293 
 294     // add buttons and "OK" labels
 295     for (i = 0; i < learn_total; i++)
 296     {
 297         char buffer[BUF_TINY];
 298         const char *label;
 299         int padding;
 300 
 301         learnkeys[i].ok = FALSE;
 302         learnkeys[i].sequence = NULL;
 303 
 304         label = _ (key_name_conv_tab[i].longname);
 305         padding = 16 - str_term_width1 (label);
 306         padding = MAX (0, padding);
 307         g_snprintf (buffer, sizeof (buffer), "%s%*s", label, padding, "");
 308 
 309         learnkeys[i].button =
 310             WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
 311         learnkeys[i].label = WIDGET (label_new (y, x + 19, NULL));
 312         group_add_widget (g, learnkeys[i].button);
 313         group_add_widget (g, learnkeys[i].label);
 314 
 315         y++;
 316         if (y == UY + ROWS)
 317         {
 318             x += COLSHIFT;
 319             y = UY;
 320         }
 321     }
 322 
 323     group_add_widget (g, hline_new (dlg_height - 8, -1, -1));
 324     group_add_widget (
 325         g,
 326         label_new (dlg_height - 7, 5,
 327                    _ ("Press all the keys mentioned here. After you have done it, check\n"
 328                       "which keys are not marked with OK. Press space on the missing\n"
 329                       "key, or click with the mouse to define it. Move around with Tab.")));
 330     group_add_widget (g, hline_new (dlg_height - 4, -1, -1));
 331     // buttons
 332     bt0 = button_new (dlg_height - 3, 1, B_ENTER, DEFPUSH_BUTTON, b0, NULL);
 333     bt1 = button_new (dlg_height - 3, 1, B_CANCEL, NORMAL_BUTTON, b1, NULL);
 334 
 335     const int bw0 = button_get_width (bt0);
 336     const int bw1 = button_get_width (bt1);
 337 
 338     const int bx0 = (dlg_width - (bw0 + bw1 + 1)) / 2;
 339     const int bx1 = bx0 + bw0 + 1;
 340 
 341     WIDGET (bt0)->rect.x = bx0;
 342     WIDGET (bt1)->rect.x = bx1;
 343 
 344     group_add_widget (g, bt0);
 345     group_add_widget (g, bt1);
 346 }
 347 
 348 /* --------------------------------------------------------------------------------------------- */
 349 
 350 static void
 351 learn_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 352 {
 353     widget_destroy (WIDGET (learn_dlg));
 354     repaint_screen ();
 355 }
 356 
 357 /* --------------------------------------------------------------------------------------------- */
 358 
 359 static void
 360 learn_save (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 361 {
 362     int i;
 363     char *section;
 364     gboolean profile_changed = FALSE;
 365 
 366     section = g_strconcat ("terminal:", getenv ("TERM"), (char *) NULL);
 367 
 368     for (i = 0; i < learn_total; i++)
 369         if (learnkeys[i].sequence != NULL)
 370         {
 371             char *esc_str;
 372 
 373             esc_str = str_escape (learnkeys[i].sequence, -1, ";\\", TRUE);
 374             mc_config_set_string_raw_value (mc_global.main_config, section,
 375                                             key_name_conv_tab[i].name, esc_str);
 376             g_free (esc_str);
 377 
 378             profile_changed = TRUE;
 379         }
 380 
 381     /* On the one hand no good idea to save the complete setup but
 382      * without 'Auto save setup' the new key-definitions will not be
 383      * saved unless the user does an 'Options/Save Setup'.
 384      * On the other hand a save-button that does not save anything to
 385      * disk is much worse.
 386      */
 387     if (profile_changed)
 388         mc_config_save_file (mc_global.main_config, NULL);
 389 
 390     g_free (section);
 391 }
 392 
 393 /* --------------------------------------------------------------------------------------------- */
 394 /*** public functions ****************************************************************************/
 395 /* --------------------------------------------------------------------------------------------- */
 396 
 397 void
 398 learn_keys (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 399 {
 400     gboolean save_old_esc_mode = old_esc_mode;
 401     gboolean save_alternate_plus_minus = mc_global.tty.alternate_plus_minus;
 402     int result;
 403 
 404     // old_esc_mode cannot work in learn keys dialog
 405     old_esc_mode = 0;
 406 
 407     /* don't translate KP_ADD, KP_SUBTRACT and
 408        KP_MULTIPLY to '+', '-' and '*' in
 409        correct_key_code */
 410     mc_global.tty.alternate_plus_minus = TRUE;
 411     application_keypad_mode ();
 412 
 413     init_learn ();
 414     result = dlg_run (learn_dlg);
 415 
 416     old_esc_mode = save_old_esc_mode;
 417     mc_global.tty.alternate_plus_minus = save_alternate_plus_minus;
 418 
 419     if (!mc_global.tty.alternate_plus_minus)
 420         numeric_keypad_mode ();
 421 
 422     if (result == B_ENTER)
 423         learn_save ();
 424 
 425     learn_done ();
 426 }
 427 
 428 /* --------------------------------------------------------------------------------------------- */

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