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

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