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 = N_ ("&Save");
 261     const char *b1 = N_ ("&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 
 274     b0 = _ (b0);
 275     b1 = _ (b1);
 276 #endif
 277 
 278     do_refresh ();
 279 
 280     learn_dlg = dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors,
 281                             learn_callback, NULL, "[Learn keys]", learn_title);
 282     g = GROUP (learn_dlg);
 283 
 284     // find first unshown button
 285     for (key = key_name_conv_tab, learn_total = 0;
 286          key->name != NULL && strcmp (key->name, "kpleft") != 0; key++, learn_total++)
 287         ;
 288 
 289     learnok = 0;
 290     learnchanged = FALSE;
 291 
 292     learnkeys = g_new (learnkey_t, learn_total);
 293 
 294     x = UX;
 295     y = UY;
 296 
 297     // add buttons and "OK" labels
 298     for (i = 0; i < learn_total; i++)
 299     {
 300         char buffer[BUF_TINY];
 301         const char *label;
 302         int padding;
 303 
 304         learnkeys[i].ok = FALSE;
 305         learnkeys[i].sequence = NULL;
 306 
 307         label = _ (key_name_conv_tab[i].longname);
 308         padding = 16 - str_term_width1 (label);
 309         padding = MAX (0, padding);
 310         g_snprintf (buffer, sizeof (buffer), "%s%*s", label, padding, "");
 311 
 312         learnkeys[i].button =
 313             WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
 314         learnkeys[i].label = WIDGET (label_new (y, x + 19, NULL));
 315         group_add_widget (g, learnkeys[i].button);
 316         group_add_widget (g, learnkeys[i].label);
 317 
 318         y++;
 319         if (y == UY + ROWS)
 320         {
 321             x += COLSHIFT;
 322             y = UY;
 323         }
 324     }
 325 
 326     group_add_widget (g, hline_new (dlg_height - 8, -1, -1));
 327     group_add_widget (
 328         g,
 329         label_new (dlg_height - 7, 5,
 330                    _ ("Press all the keys mentioned here. After you have done it, check\n"
 331                       "which keys are not marked with OK. Press space on the missing\n"
 332                       "key, or click with the mouse to define it. Move around with Tab.")));
 333     group_add_widget (g, hline_new (dlg_height - 4, -1, -1));
 334     // buttons
 335     bt0 = button_new (dlg_height - 3, 1, B_ENTER, DEFPUSH_BUTTON, b0, NULL);
 336     bt1 = button_new (dlg_height - 3, 1, B_CANCEL, NORMAL_BUTTON, b1, NULL);
 337 
 338     const int bw0 = button_get_width (bt0);
 339     const int bw1 = button_get_width (bt1);
 340 
 341     const int bx0 = (dlg_width - (bw0 + bw1 + 1)) / 2;
 342     const int bx1 = bx0 + bw0 + 1;
 343 
 344     WIDGET (bt0)->rect.x = bx0;
 345     WIDGET (bt1)->rect.x = bx1;
 346 
 347     group_add_widget (g, bt0);
 348     group_add_widget (g, bt1);
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */
 352 
 353 static void
 354 learn_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 355 {
 356     widget_destroy (WIDGET (learn_dlg));
 357     repaint_screen ();
 358 }
 359 
 360 /* --------------------------------------------------------------------------------------------- */
 361 
 362 static void
 363 learn_save (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 364 {
 365     int i;
 366     char *section;
 367     gboolean profile_changed = FALSE;
 368 
 369     section = g_strconcat ("terminal:", getenv ("TERM"), (char *) NULL);
 370 
 371     for (i = 0; i < learn_total; i++)
 372         if (learnkeys[i].sequence != NULL)
 373         {
 374             char *esc_str;
 375 
 376             esc_str = str_escape (learnkeys[i].sequence, -1, ";\\", TRUE);
 377             mc_config_set_string_raw_value (mc_global.main_config, section,
 378                                             key_name_conv_tab[i].name, esc_str);
 379             g_free (esc_str);
 380 
 381             profile_changed = TRUE;
 382         }
 383 
 384     /* On the one hand no good idea to save the complete setup but
 385      * without 'Auto save setup' the new key-definitions will not be
 386      * saved unless the user does an 'Options/Save Setup'.
 387      * On the other hand a save-button that does not save anything to
 388      * disk is much worse.
 389      */
 390     if (profile_changed)
 391         mc_config_save_file (mc_global.main_config, NULL);
 392 
 393     g_free (section);
 394 }
 395 
 396 /* --------------------------------------------------------------------------------------------- */
 397 /*** public functions ****************************************************************************/
 398 /* --------------------------------------------------------------------------------------------- */
 399 
 400 void
 401 learn_keys (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 402 {
 403     gboolean save_old_esc_mode = old_esc_mode;
 404     gboolean save_alternate_plus_minus = mc_global.tty.alternate_plus_minus;
 405     int result;
 406 
 407     // old_esc_mode cannot work in learn keys dialog
 408     old_esc_mode = 0;
 409 
 410     /* don't translate KP_ADD, KP_SUBTRACT and
 411        KP_MULTIPLY to '+', '-' and '*' in
 412        correct_key_code */
 413     mc_global.tty.alternate_plus_minus = TRUE;
 414     application_keypad_mode ();
 415 
 416     init_learn ();
 417     result = dlg_run (learn_dlg);
 418 
 419     old_esc_mode = save_old_esc_mode;
 420     mc_global.tty.alternate_plus_minus = save_alternate_plus_minus;
 421 
 422     if (!mc_global.tty.alternate_plus_minus)
 423         numeric_keypad_mode ();
 424 
 425     if (result == B_ENTER)
 426         learn_save ();
 427 
 428     learn_done ();
 429 }
 430 
 431 /* --------------------------------------------------------------------------------------------- */

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