Merge branch '3243_usmenu_view_man'
[midnight-commander.git] / src / learn.c
blobb6faf0ae0c5dd3c86510382c55ddc85a4d9524c6
1 /*
2 Learn keys
4 Copyright (C) 1995-2014
5 Free Software Foundation, Inc.
7 Written by:
8 Jakub Jelinek, 1995
9 Andrew Borodin <aborodin@vmail.ru>, 2012, 2013
11 This file is part of the Midnight Commander.
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.
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.
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/>.
27 /** \file learn.c
28 * \brief Source: learn keys module
31 #include <config.h>
33 #include <stdlib.h>
35 #include "lib/global.h"
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"
45 #include "setup.h"
46 #include "learn.h"
48 /*** global variables ****************************************************************************/
50 /*** file scope macro definitions ****************************************************************/
52 #define UX 4
53 #define UY 2
55 #define ROWS 13
56 #define COLSHIFT 23
58 /*** file scope type declarations ****************************************************************/
60 typedef struct
62 Widget *button;
63 Widget *label;
64 gboolean ok;
65 char *sequence;
66 } learnkey_t;
68 /*** file scope variables ************************************************************************/
70 static WDialog *learn_dlg;
71 static const char *learn_title = N_("Learn keys");
73 static learnkey_t *learnkeys = NULL;
74 static int learn_total;
75 static int learnok;
76 static gboolean learnchanged = FALSE;
78 /*** file scope functions ************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
81 static int
82 learn_button (WButton * button, int action)
84 WDialog *d;
85 char *seq;
87 (void) button;
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);
100 seq = learn_key ();
101 if (seq != NULL)
103 /* Esc hides the dialog and do not allow definitions of
104 * regular characters
106 gboolean seq_ok = FALSE;
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 > '~'))
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);
118 if (!seq_ok)
119 message (D_NORMAL, _("Cannot accept this key"), _("You have entered \"%s\""), seq);
121 g_free (seq);
124 dlg_run_done (d);
125 dlg_destroy (d);
127 dlg_select_widget (learnkeys[action - B_USER].button);
129 return 0; /* Do not kill learn_dlg */
132 /* --------------------------------------------------------------------------------------------- */
134 static gboolean
135 learn_move (gboolean right)
137 int i, totalcols;
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))
143 if (right)
145 if (i < learn_total - ROWS)
146 i += ROWS;
147 else
148 i %= ROWS;
150 else
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;
159 dlg_select_widget (learnkeys[i].button);
160 return TRUE;
163 return FALSE;
166 /* --------------------------------------------------------------------------------------------- */
168 static gboolean
169 learn_check_key (int c)
171 int i;
173 for (i = 0; i < learn_total; i++)
175 if (key_name_conv_tab[i].code != c || learnkeys[i].ok)
176 continue;
178 dlg_select_widget (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)
185 learn_dlg->ret_value = B_CANCEL;
186 if (learnchanged)
188 if (query_dialog (learn_title,
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;
195 else
197 message (D_ERROR, learn_title,
199 ("Great! You have a complete terminal database!\n"
200 "All your keys work well."));
202 dlg_stop (learn_dlg);
204 return TRUE;
207 switch (c)
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_one_down (learn_dlg);
217 return TRUE;
218 case 'k':
219 dlg_one_up (learn_dlg);
220 return TRUE;
223 /* Prevent from disappearing if a non-defined sequence is pressed
224 and contains a button hotkey. Only recognize hotkeys with ALT. */
225 return (c < 255 && g_ascii_isalnum (c));
228 /* --------------------------------------------------------------------------------------------- */
230 static cb_ret_t
231 learn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
233 switch (msg)
235 case MSG_KEY:
236 return learn_check_key (parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
238 default:
239 return dlg_default_callback (w, sender, msg, parm, data);
243 /* --------------------------------------------------------------------------------------------- */
245 static void
246 init_learn (void)
248 const int dlg_width = 78;
249 const int dlg_height = 23;
251 /* buttons */
252 int bx0, bx1;
253 const char *b0 = N_("&Save");
254 const char *b1 = N_("&Cancel");
255 int bl0, bl1;
257 int x, y, i;
258 const key_code_name_t *key;
260 #ifdef ENABLE_NLS
261 static gboolean i18n_flag = FALSE;
262 if (!i18n_flag)
264 learn_title = _(learn_title);
265 i18n_flag = TRUE;
268 b0 = _(b0);
269 b1 = _(b1);
270 #endif /* ENABLE_NLS */
272 do_refresh ();
274 learn_dlg =
275 dlg_create (TRUE, 0, 0, dlg_height, dlg_width, dialog_colors, learn_callback, NULL,
276 "[Learn keys]", learn_title, DLG_CENTER);
278 /* find first unshown button */
279 for (key = key_name_conv_tab, learn_total = 0;
280 key->name != NULL && strcmp (key->name, "kpleft") != 0; key++, learn_total++)
283 learnok = 0;
284 learnchanged = FALSE;
286 learnkeys = g_new (learnkey_t, learn_total);
288 x = UX;
289 y = UY;
291 /* add buttons and "OK" labels */
292 for (i = 0; i < learn_total; i++)
294 char buffer[BUF_TINY];
295 const char *label;
296 int padding;
298 learnkeys[i].ok = FALSE;
299 learnkeys[i].sequence = NULL;
301 label = _(key_name_conv_tab[i].longname);
302 padding = 16 - str_term_width1 (label);
303 padding = max (0, padding);
304 g_snprintf (buffer, sizeof (buffer), "%s%*s", label, padding, "");
306 learnkeys[i].button =
307 WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
308 learnkeys[i].label = WIDGET (label_new (y, x + 19, ""));
309 add_widget (learn_dlg, learnkeys[i].button);
310 add_widget (learn_dlg, learnkeys[i].label);
312 y++;
313 if (y == UY + ROWS)
315 x += COLSHIFT;
316 y = UY;
320 add_widget (learn_dlg, hline_new (dlg_height - 8, -1, -1));
321 add_widget (learn_dlg,
322 label_new (dlg_height - 7, 5,
323 _("Press all the keys mentioned here. After you have done it, check\n"
324 "which keys are not marked with OK. Press space on the missing\n"
325 "key, or click with the mouse to define it. Move around with Tab.")));
326 add_widget (learn_dlg, hline_new (dlg_height - 4, -1, -1));
327 /* buttons */
328 bl0 = str_term_width1 (b0) + 5; /* default button */
329 bl1 = str_term_width1 (b1) + 3; /* normal button */
330 bx0 = (dlg_width - (bl0 + bl1 + 1)) / 2;
331 bx1 = bx0 + bl0 + 1;
332 add_widget (learn_dlg, button_new (dlg_height - 3, bx0, B_ENTER, DEFPUSH_BUTTON, b0, NULL));
333 add_widget (learn_dlg, button_new (dlg_height - 3, bx1, B_CANCEL, NORMAL_BUTTON, b1, NULL));
336 /* --------------------------------------------------------------------------------------------- */
338 static void
339 learn_done (void)
341 dlg_destroy (learn_dlg);
342 repaint_screen ();
345 /* --------------------------------------------------------------------------------------------- */
347 static void
348 learn_save (void)
350 int i;
351 char *section;
352 gboolean profile_changed = FALSE;
354 section = g_strconcat ("terminal:", getenv ("TERM"), (char *) NULL);
356 for (i = 0; i < learn_total; i++)
357 if (learnkeys[i].sequence != NULL)
359 char *esc_str;
361 esc_str = strutils_escape (learnkeys[i].sequence, -1, ";\\", TRUE);
362 mc_config_set_string_raw_value (mc_main_config, section, key_name_conv_tab[i].name,
363 esc_str);
364 g_free (esc_str);
366 profile_changed = TRUE;
369 /* On the one hand no good idea to save the complete setup but
370 * without 'Auto save setup' the new key-definitions will not be
371 * saved unless the user does an 'Options/Save Setup'.
372 * On the other hand a save-button that does not save anything to
373 * disk is much worse.
375 if (profile_changed)
376 mc_config_save_file (mc_main_config, NULL);
378 g_free (section);
381 /* --------------------------------------------------------------------------------------------- */
382 /*** public functions ****************************************************************************/
383 /* --------------------------------------------------------------------------------------------- */
385 void
386 learn_keys (void)
388 int save_old_esc_mode = old_esc_mode;
389 gboolean save_alternate_plus_minus = mc_global.tty.alternate_plus_minus;
390 int result;
392 /* old_esc_mode cannot work in learn keys dialog */
393 old_esc_mode = 0;
395 /* don't translate KP_ADD, KP_SUBTRACT and
396 KP_MULTIPLY to '+', '-' and '*' in
397 correct_key_code */
398 mc_global.tty.alternate_plus_minus = TRUE;
399 application_keypad_mode ();
401 init_learn ();
402 result = dlg_run (learn_dlg);
404 old_esc_mode = save_old_esc_mode;
405 mc_global.tty.alternate_plus_minus = save_alternate_plus_minus;
407 if (!mc_global.tty.alternate_plus_minus)
408 numeric_keypad_mode ();
410 if (result == B_ENTER)
411 learn_save ();
413 learn_done ();
416 /* --------------------------------------------------------------------------------------------- */