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 <http://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/util.h"           /* convert_controls() */
  42 #include "lib/widget.h"
  43 
  44 #include "setup.h"
  45 #include "learn.h"
  46 
  47 /*** global variables ****************************************************************************/
  48 
  49 /*** file scope macro definitions ****************************************************************/
  50 
  51 #define UX 4
  52 #define UY 2
  53 
  54 #define ROWS     13
  55 #define COLSHIFT 23
  56 
  57 /*** file scope type declarations ****************************************************************/
  58 
  59 typedef struct
  60 {
  61     Widget *button;
  62     Widget *label;
  63     gboolean ok;
  64     char *sequence;
  65 } learnkey_t;
  66 
  67 /*** forward declarations (file scope functions) *************************************************/
  68 
  69 /*** file scope variables ************************************************************************/
  70 
  71 static WDialog *learn_dlg;
  72 static const char *learn_title = N_("Learn keys");
  73 
  74 static learnkey_t *learnkeys = NULL;
  75 static int learn_total;
  76 static int learnok;
  77 static gboolean learnchanged = FALSE;
  78 
  79 /* --------------------------------------------------------------------------------------------- */
  80 /*** file scope functions ************************************************************************/
  81 /* --------------------------------------------------------------------------------------------- */
  82 
  83 static int
  84 learn_button (WButton *button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
  85 {
  86     WDialog *d;
  87     char *seq;
  88 
  89     (void) button;
  90 
  91     d = create_message (D_ERROR, _("Teach me a key"),
  92                         _("Please press the %s\n"
  93                           "and then wait until this message disappears.\n\n"
  94                           "Then, press it again to see if OK appears\n"
  95                           "next to its button.\n\n"
  96                           "If you want to escape, press a single Escape key\n"
  97                           "and wait as well."), _(key_name_conv_tab[action - B_USER].longname));
  98     mc_refresh ();
  99     if (learnkeys[action - B_USER].sequence != NULL)
 100         MC_PTR_FREE (learnkeys[action - B_USER].sequence);
 101 
 102     seq = learn_key ();
 103     if (seq != NULL)
 104     {
 105         /* Esc hides the dialog and do not allow definitions of
 106          * regular characters
 107          */
 108         gboolean seq_ok = FALSE;
 109 
 110         if (strcmp (seq, "\\e") != 0 && strcmp (seq, "\\e\\e") != 0
 111             && strcmp (seq, "^m") != 0 && strcmp (seq, "^i") != 0
 112             && (seq[1] != '\0' || *seq < ' ' || *seq > '~'))
 113         {
 114             learnchanged = TRUE;
 115             learnkeys[action - B_USER].sequence = seq;
 116             seq = convert_controls (seq);
 117             seq_ok = define_sequence (key_name_conv_tab[action - B_USER].code, seq, MCKEY_NOACTION);
 118         }
 119 
 120         if (!seq_ok)
 121             message (D_NORMAL, _("Warning"), _("Cannot accept this key.\nYou have entered \"%s\""),
 122                      seq);
 123 
 124         g_free (seq);
 125     }
 126 
 127     dlg_run_done (d);
 128     widget_destroy (WIDGET (d));
 129 
 130     widget_select (learnkeys[action - B_USER].button);
 131 
 132     return 0;                   /* Do not kill learn_dlg */
 133 }
 134 
 135 /* --------------------------------------------------------------------------------------------- */
 136 
 137 static gboolean
 138 learn_move (gboolean right)
     /* [previous][next][first][last][top][bottom][index][help]  */
 139 {
 140     int i, totalcols;
 141 
 142     totalcols = (learn_total - 1) / ROWS + 1;
 143     for (i = 0; i < learn_total; i++)
 144         if (learnkeys[i].button == WIDGET (GROUP (learn_dlg)->current->data))
 145         {
 146             if (right)
 147             {
 148                 if (i < learn_total - ROWS)
 149                     i += ROWS;
 150                 else
 151                     i %= ROWS;
 152             }
 153             else
 154             {
 155                 if (i / ROWS != 0)
 156                     i -= ROWS;
 157                 else if (i + (totalcols - 1) * ROWS >= learn_total)
 158                     i += (totalcols - 2) * ROWS;
 159                 else
 160                     i += (totalcols - 1) * ROWS;
 161             }
 162             widget_select (learnkeys[i].button);
 163             return TRUE;
 164         }
 165 
 166     return FALSE;
 167 }
 168 
 169 /* --------------------------------------------------------------------------------------------- */
 170 
 171 static gboolean
 172 learn_check_key (int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 173 {
 174     int i;
 175 
 176     for (i = 0; i < learn_total; i++)
 177     {
 178         if (key_name_conv_tab[i].code != c || learnkeys[i].ok)
 179             continue;
 180 
 181         widget_select (learnkeys[i].button);
 182         /* TRANSLATORS: This label appears near learned keys.  Keep it short.  */
 183         label_set_text (LABEL (learnkeys[i].label), _("OK"));
 184         learnkeys[i].ok = TRUE;
 185         learnok++;
 186         if (learnok >= learn_total)
 187         {
 188             learn_dlg->ret_value = B_CANCEL;
 189             if (learnchanged)
 190             {
 191                 if (query_dialog (learn_title,
 192                                   _
 193                                   ("It seems that all your keys already\n"
 194                                    "work fine. That's great."), D_ERROR, 2,
 195                                   _("&Save"), _("&Discard")) == 0)
 196                     learn_dlg->ret_value = B_ENTER;
 197             }
 198             else
 199             {
 200                 message (D_ERROR, learn_title, "%s",
 201                          _
 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     int bx0, bx1;
 260     const char *b0 = N_("&Save");
 261     const char *b1 = N_("&Cancel");
 262     int bl0, bl1;
 263 
 264     int x, y, i;
 265     const key_code_name_t *key;
 266 
 267 #ifdef ENABLE_NLS
 268     static gboolean i18n_flag = FALSE;
 269     if (!i18n_flag)
 270     {
 271         learn_title = _(learn_title);
 272         i18n_flag = TRUE;
 273     }
 274 
 275     b0 = _(b0);
 276     b1 = _(b1);
 277 #endif /* ENABLE_NLS */
 278 
 279     do_refresh ();
 280 
 281     learn_dlg =
 282         dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors,
 283                     learn_callback, NULL, "[Learn keys]", learn_title);
 284     g = GROUP (learn_dlg);
 285 
 286     /* find first unshown button */
 287     for (key = key_name_conv_tab, learn_total = 0;
 288          key->name != NULL && strcmp (key->name, "kpleft") != 0; key++, learn_total++)
 289         ;
 290 
 291     learnok = 0;
 292     learnchanged = FALSE;
 293 
 294     learnkeys = g_new (learnkey_t, learn_total);
 295 
 296     x = UX;
 297     y = UY;
 298 
 299     /* add buttons and "OK" labels */
 300     for (i = 0; i < learn_total; i++)
 301     {
 302         char buffer[BUF_TINY];
 303         const char *label;
 304         int padding;
 305 
 306         learnkeys[i].ok = FALSE;
 307         learnkeys[i].sequence = NULL;
 308 
 309         label = _(key_name_conv_tab[i].longname);
 310         padding = 16 - str_term_width1 (label);
 311         padding = MAX (0, padding);
 312         g_snprintf (buffer, sizeof (buffer), "%s%*s", label, padding, "");
 313 
 314         learnkeys[i].button =
 315             WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
 316         learnkeys[i].label = WIDGET (label_new (y, x + 19, NULL));
 317         group_add_widget (g, learnkeys[i].button);
 318         group_add_widget (g, learnkeys[i].label);
 319 
 320         y++;
 321         if (y == UY + ROWS)
 322         {
 323             x += COLSHIFT;
 324             y = UY;
 325         }
 326     }
 327 
 328     group_add_widget (g, hline_new (dlg_height - 8, -1, -1));
 329     group_add_widget (g, label_new (dlg_height - 7, 5,
 330                                     _
 331                                     ("Press all the keys mentioned here. After you have done it, check\n"
 332                                      "which keys are not marked with OK. Press space on the missing\n"
 333                                      "key, or click with the mouse to define it. Move around with Tab.")));
 334     group_add_widget (g, hline_new (dlg_height - 4, -1, -1));
 335     /* buttons */
 336     bl0 = str_term_width1 (b0) + 5;     /* default button */
 337     bl1 = str_term_width1 (b1) + 3;     /* normal button */
 338     bx0 = (dlg_width - (bl0 + bl1 + 1)) / 2;
 339     bx1 = bx0 + bl0 + 1;
 340     group_add_widget (g, button_new (dlg_height - 3, bx0, B_ENTER, DEFPUSH_BUTTON, b0, NULL));
 341     group_add_widget (g, button_new (dlg_height - 3, bx1, B_CANCEL, NORMAL_BUTTON, b1, NULL));
 342 }
 343 
 344 /* --------------------------------------------------------------------------------------------- */
 345 
 346 static void
 347 learn_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 348 {
 349     widget_destroy (WIDGET (learn_dlg));
 350     repaint_screen ();
 351 }
 352 
 353 /* --------------------------------------------------------------------------------------------- */
 354 
 355 static void
 356 learn_save (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 357 {
 358     int i;
 359     char *section;
 360     gboolean profile_changed = FALSE;
 361 
 362     section = g_strconcat ("terminal:", getenv ("TERM"), (char *) NULL);
 363 
 364     for (i = 0; i < learn_total; i++)
 365         if (learnkeys[i].sequence != NULL)
 366         {
 367             char *esc_str;
 368 
 369             esc_str = str_escape (learnkeys[i].sequence, -1, ";\\", TRUE);
 370             mc_config_set_string_raw_value (mc_global.main_config, section,
 371                                             key_name_conv_tab[i].name, esc_str);
 372             g_free (esc_str);
 373 
 374             profile_changed = TRUE;
 375         }
 376 
 377     /* On the one hand no good idea to save the complete setup but 
 378      * without 'Auto save setup' the new key-definitions will not be 
 379      * saved unless the user does an 'Options/Save Setup'. 
 380      * On the other hand a save-button that does not save anything to 
 381      * disk is much worse.
 382      */
 383     if (profile_changed)
 384         mc_config_save_file (mc_global.main_config, NULL);
 385 
 386     g_free (section);
 387 }
 388 
 389 /* --------------------------------------------------------------------------------------------- */
 390 /*** public functions ****************************************************************************/
 391 /* --------------------------------------------------------------------------------------------- */
 392 
 393 void
 394 learn_keys (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 395 {
 396     gboolean save_old_esc_mode = old_esc_mode;
 397     gboolean save_alternate_plus_minus = mc_global.tty.alternate_plus_minus;
 398     int result;
 399 
 400     /* old_esc_mode cannot work in learn keys dialog */
 401     old_esc_mode = 0;
 402 
 403     /* don't translate KP_ADD, KP_SUBTRACT and
 404        KP_MULTIPLY to '+', '-' and '*' in
 405        correct_key_code */
 406     mc_global.tty.alternate_plus_minus = TRUE;
 407     application_keypad_mode ();
 408 
 409     init_learn ();
 410     result = dlg_run (learn_dlg);
 411 
 412     old_esc_mode = save_old_esc_mode;
 413     mc_global.tty.alternate_plus_minus = save_alternate_plus_minus;
 414 
 415     if (!mc_global.tty.alternate_plus_minus)
 416         numeric_keypad_mode ();
 417 
 418     if (result == B_ENTER)
 419         learn_save ();
 420 
 421     learn_done ();
 422 }
 423 
 424 /* --------------------------------------------------------------------------------------------- */

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