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-2024
   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, _("Cannot accept this key"), _("You have entered \"%s\""), seq);
 122 
 123         g_free (seq);
 124     }
 125 
 126     dlg_run_done (d);
 127     widget_destroy (WIDGET (d));
 128 
 129     widget_select (learnkeys[action - B_USER].button);
 130 
 131     return 0;                   /* Do not kill learn_dlg */
 132 }
 133 
 134 /* --------------------------------------------------------------------------------------------- */
 135 
 136 static gboolean
 137 learn_move (gboolean right)
     /* [previous][next][first][last][top][bottom][index][help]  */
 138 {
 139     int i, totalcols;
 140 
 141     totalcols = (learn_total - 1) / ROWS + 1;
 142     for (i = 0; i < learn_total; i++)
 143         if (learnkeys[i].button == WIDGET (GROUP (learn_dlg)->current->data))
 144         {
 145             if (right)
 146             {
 147                 if (i < learn_total - ROWS)
 148                     i += ROWS;
 149                 else
 150                     i %= ROWS;
 151             }
 152             else
 153             {
 154                 if (i / ROWS != 0)
 155                     i -= ROWS;
 156                 else if (i + (totalcols - 1) * ROWS >= learn_total)
 157                     i += (totalcols - 2) * ROWS;
 158                 else
 159                     i += (totalcols - 1) * ROWS;
 160             }
 161             widget_select (learnkeys[i].button);
 162             return TRUE;
 163         }
 164 
 165     return FALSE;
 166 }
 167 
 168 /* --------------------------------------------------------------------------------------------- */
 169 
 170 static gboolean
 171 learn_check_key (int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 172 {
 173     int i;
 174 
 175     for (i = 0; i < learn_total; i++)
 176     {
 177         if (key_name_conv_tab[i].code != c || learnkeys[i].ok)
 178             continue;
 179 
 180         widget_select (learnkeys[i].button);
 181         /* TRANSLATORS: This label appears near learned keys.  Keep it short.  */
 182         label_set_text (LABEL (learnkeys[i].label), _("OK"));
 183         learnkeys[i].ok = TRUE;
 184         learnok++;
 185         if (learnok >= learn_total)
 186         {
 187             learn_dlg->ret_value = B_CANCEL;
 188             if (learnchanged)
 189             {
 190                 if (query_dialog (learn_title,
 191                                   _
 192                                   ("It seems that all your keys already\n"
 193                                    "work fine. That's great."), D_ERROR, 2,
 194                                   _("&Save"), _("&Discard")) == 0)
 195                     learn_dlg->ret_value = B_ENTER;
 196             }
 197             else
 198             {
 199                 message (D_ERROR, learn_title, "%s",
 200                          _
 201                          ("Great! You have a complete terminal database!\n"
 202                           "All your keys work well."));
 203             }
 204             dlg_close (learn_dlg);
 205         }
 206         return TRUE;
 207     }
 208 
 209     switch (c)
 210     {
 211     case KEY_LEFT:
 212     case 'h':
 213         return learn_move (FALSE);
 214     case KEY_RIGHT:
 215     case 'l':
 216         return learn_move (TRUE);
 217     case 'j':
 218         group_select_next_widget (GROUP (learn_dlg));
 219         return TRUE;
 220     case 'k':
 221         group_select_prev_widget (GROUP (learn_dlg));
 222         return TRUE;
 223     default:
 224         break;
 225     }
 226 
 227     /* Prevent from disappearing if a non-defined sequence is pressed
 228        and contains a button hotkey.  Only recognize hotkeys with ALT.  */
 229     return (c < 255 && g_ascii_isalnum (c));
 230 }
 231 
 232 /* --------------------------------------------------------------------------------------------- */
 233 
 234 static cb_ret_t
 235 learn_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 236 {
 237     switch (msg)
 238     {
 239     case MSG_KEY:
 240         return learn_check_key (parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
 241 
 242     default:
 243         return dlg_default_callback (w, sender, msg, parm, data);
 244     }
 245 }
 246 
 247 /* --------------------------------------------------------------------------------------------- */
 248 
 249 static void
 250 init_learn (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 251 {
 252     WGroup *g;
 253 
 254     const int dlg_width = 78;
 255     const int dlg_height = 23;
 256 
 257     /* buttons */
 258     int bx0, bx1;
 259     const char *b0 = N_("&Save");
 260     const char *b1 = N_("&Cancel");
 261     int bl0, bl1;
 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 /* ENABLE_NLS */
 277 
 278     do_refresh ();
 279 
 280     learn_dlg =
 281         dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors,
 282                     learn_callback, NULL, "[Learn keys]", learn_title);
 283     g = GROUP (learn_dlg);
 284 
 285     /* find first unshown button */
 286     for (key = key_name_conv_tab, learn_total = 0;
 287          key->name != NULL && strcmp (key->name, "kpleft") != 0; key++, learn_total++)
 288         ;
 289 
 290     learnok = 0;
 291     learnchanged = FALSE;
 292 
 293     learnkeys = g_new (learnkey_t, learn_total);
 294 
 295     x = UX;
 296     y = UY;
 297 
 298     /* add buttons and "OK" labels */
 299     for (i = 0; i < learn_total; i++)
 300     {
 301         char buffer[BUF_TINY];
 302         const char *label;
 303         int padding;
 304 
 305         learnkeys[i].ok = FALSE;
 306         learnkeys[i].sequence = NULL;
 307 
 308         label = _(key_name_conv_tab[i].longname);
 309         padding = 16 - str_term_width1 (label);
 310         padding = MAX (0, padding);
 311         g_snprintf (buffer, sizeof (buffer), "%s%*s", label, padding, "");
 312 
 313         learnkeys[i].button =
 314             WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
 315         learnkeys[i].label = WIDGET (label_new (y, x + 19, NULL));
 316         group_add_widget (g, learnkeys[i].button);
 317         group_add_widget (g, learnkeys[i].label);
 318 
 319         y++;
 320         if (y == UY + ROWS)
 321         {
 322             x += COLSHIFT;
 323             y = UY;
 324         }
 325     }
 326 
 327     group_add_widget (g, hline_new (dlg_height - 8, -1, -1));
 328     group_add_widget (g, label_new (dlg_height - 7, 5,
 329                                     _
 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     bl0 = str_term_width1 (b0) + 5;     /* default button */
 336     bl1 = str_term_width1 (b1) + 3;     /* normal button */
 337     bx0 = (dlg_width - (bl0 + bl1 + 1)) / 2;
 338     bx1 = bx0 + bl0 + 1;
 339     group_add_widget (g, button_new (dlg_height - 3, bx0, B_ENTER, DEFPUSH_BUTTON, b0, NULL));
 340     group_add_widget (g, button_new (dlg_height - 3, bx1, B_CANCEL, NORMAL_BUTTON, b1, NULL));
 341 }
 342 
 343 /* --------------------------------------------------------------------------------------------- */
 344 
 345 static void
 346 learn_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 347 {
 348     widget_destroy (WIDGET (learn_dlg));
 349     repaint_screen ();
 350 }
 351 
 352 /* --------------------------------------------------------------------------------------------- */
 353 
 354 static void
 355 learn_save (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 356 {
 357     int i;
 358     char *section;
 359     gboolean profile_changed = FALSE;
 360 
 361     section = g_strconcat ("terminal:", getenv ("TERM"), (char *) NULL);
 362 
 363     for (i = 0; i < learn_total; i++)
 364         if (learnkeys[i].sequence != NULL)
 365         {
 366             char *esc_str;
 367 
 368             esc_str = str_escape (learnkeys[i].sequence, -1, ";\\", TRUE);
 369             mc_config_set_string_raw_value (mc_global.main_config, section,
 370                                             key_name_conv_tab[i].name, esc_str);
 371             g_free (esc_str);
 372 
 373             profile_changed = TRUE;
 374         }
 375 
 376     /* On the one hand no good idea to save the complete setup but 
 377      * without 'Auto save setup' the new key-definitions will not be 
 378      * saved unless the user does an 'Options/Save Setup'. 
 379      * On the other hand a save-button that does not save anything to 
 380      * disk is much worse.
 381      */
 382     if (profile_changed)
 383         mc_config_save_file (mc_global.main_config, NULL);
 384 
 385     g_free (section);
 386 }
 387 
 388 /* --------------------------------------------------------------------------------------------- */
 389 /*** public functions ****************************************************************************/
 390 /* --------------------------------------------------------------------------------------------- */
 391 
 392 void
 393 learn_keys (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 394 {
 395     gboolean save_old_esc_mode = old_esc_mode;
 396     gboolean save_alternate_plus_minus = mc_global.tty.alternate_plus_minus;
 397     int result;
 398 
 399     /* old_esc_mode cannot work in learn keys dialog */
 400     old_esc_mode = 0;
 401 
 402     /* don't translate KP_ADD, KP_SUBTRACT and
 403        KP_MULTIPLY to '+', '-' and '*' in
 404        correct_key_code */
 405     mc_global.tty.alternate_plus_minus = TRUE;
 406     application_keypad_mode ();
 407 
 408     init_learn ();
 409     result = dlg_run (learn_dlg);
 410 
 411     old_esc_mode = save_old_esc_mode;
 412     mc_global.tty.alternate_plus_minus = save_alternate_plus_minus;
 413 
 414     if (!mc_global.tty.alternate_plus_minus)
 415         numeric_keypad_mode ();
 416 
 417     if (result == B_ENTER)
 418         learn_save ();
 419 
 420     learn_done ();
 421 }
 422 
 423 /* --------------------------------------------------------------------------------------------- */

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