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

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