src/learch.c: cleanup.
[midnight-commander.git] / src / learn.c
blobdff8c809feaf47020ddab44f29148f3d1966ca20
1 /*
2 Learn keys
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2007, 2009, 2011, 2012
6 The Free Software Foundation, Inc.
8 Written by:
9 Jakub Jelinek, 1995
10 Andrew Borodin <aborodin@vmail.ru>, 2012
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file learn.c
29 * \brief Source: learn keys module
32 #include <config.h>
34 #include <stdlib.h>
36 #include "lib/global.h"
38 #include "lib/tty/tty.h"
39 #include "lib/tty/key.h"
40 #include "lib/mcconfig.h"
41 #include "lib/strescape.h"
42 #include "lib/strutil.h"
43 #include "lib/util.h" /* convert_controls() */
44 #include "lib/widget.h"
46 #include "setup.h"
47 #include "learn.h"
49 /*** global variables ****************************************************************************/
51 /*** file scope macro definitions ****************************************************************/
53 #define UX 4
54 #define UY 2
56 #define ROWS 13
57 #define COLSHIFT 23
59 #define BUTTONS 2
61 /*** file scope type declarations ****************************************************************/
63 typedef struct
65 Widget *button;
66 Widget *label;
67 gboolean ok;
68 char *sequence;
69 } learnkey_t;
71 /*** file scope variables ************************************************************************/
73 static Dlg_head *learn_dlg;
74 static const char *learn_title = N_("Learn keys");
76 static learnkey_t *learnkeys = NULL;
77 static int learn_total;
78 static int learnok;
79 static gboolean learnchanged = FALSE;
81 /*** file scope functions ************************************************************************/
82 /* --------------------------------------------------------------------------------------------- */
84 static int
85 learn_button (WButton * button, int action)
87 Dlg_head *d;
88 char *seq;
90 (void) button;
92 d = create_message (D_ERROR, _("Teach me a key"),
93 _("Please press the %s\n"
94 "and then wait until this message disappears.\n\n"
95 "Then, press it again to see if OK appears\n"
96 "next to its button.\n\n"
97 "If you want to escape, press a single Escape key\n"
98 "and wait as well."), _(key_name_conv_tab[action - B_USER].longname));
99 mc_refresh ();
100 if (learnkeys[action - B_USER].sequence != NULL)
102 g_free (learnkeys[action - B_USER].sequence);
103 learnkeys[action - B_USER].sequence = NULL;
106 seq = learn_key ();
107 if (seq != NULL)
109 /* Esc hides the dialog and do not allow definitions of
110 * regular characters
112 gboolean seq_ok = FALSE;
114 if (*seq != '\0' && strcmp (seq, "\\e") != 0 && strcmp (seq, "\\e\\e") != 0
115 && strcmp (seq, "^m") != 0 && strcmp (seq, "^i") != 0
116 && (seq[1] != '\0' || *seq < ' ' || *seq > '~'))
118 learnchanged = TRUE;
119 learnkeys[action - B_USER].sequence = seq;
120 seq = convert_controls (seq);
121 seq_ok = define_sequence (key_name_conv_tab[action - B_USER].code, seq, MCKEY_NOACTION);
124 if (!seq_ok)
125 message (D_NORMAL, _("Cannot accept this key"), _("You have entered \"%s\""), seq);
127 g_free (seq);
130 dlg_run_done (d);
131 destroy_dlg (d);
133 dlg_select_widget (learnkeys[action - B_USER].button);
135 return 0; /* Do not kill learn_dlg */
138 /* --------------------------------------------------------------------------------------------- */
140 static gboolean
141 learn_move (gboolean right)
143 int i, totalcols;
145 totalcols = (learn_total - 1) / ROWS + 1;
146 for (i = 0; i < learn_total; i++)
147 if (learnkeys[i].button == WIDGET (learn_dlg->current->data))
149 if (right)
151 if (i < learn_total - ROWS)
152 i += ROWS;
153 else
154 i %= ROWS;
156 else
158 if (i / ROWS != 0)
159 i -= ROWS;
160 else if (i + (totalcols - 1) * ROWS >= learn_total)
161 i += (totalcols - 2) * ROWS;
162 else
163 i += (totalcols - 1) * ROWS;
165 dlg_select_widget (learnkeys[i].button);
166 return TRUE;
169 return FALSE;
172 /* --------------------------------------------------------------------------------------------- */
174 static gboolean
175 learn_check_key (int c)
177 int i;
179 for (i = 0; i < learn_total; i++)
181 if (key_name_conv_tab[i].code != c || learnkeys[i].ok)
182 continue;
184 dlg_select_widget (learnkeys[i].button);
185 /* TRANSLATORS: This label appears near learned keys. Keep it short. */
186 label_set_text ((WLabel *) learnkeys[i].label, _("OK"));
187 learnkeys[i].ok = TRUE;
188 learnok++;
189 if (learnok >= learn_total)
191 learn_dlg->ret_value = B_CANCEL;
192 if (learnchanged)
194 if (query_dialog (learn_title,
196 ("It seems that all your keys already\n"
197 "work fine. That's great."), D_ERROR, 2,
198 _("&Save"), _("&Discard")) == 0)
199 learn_dlg->ret_value = B_ENTER;
201 else
203 message (D_ERROR, learn_title,
205 ("Great! You have a complete terminal database!\n"
206 "All your keys work well."));
208 dlg_stop (learn_dlg);
210 return TRUE;
213 switch (c)
215 case KEY_LEFT:
216 case 'h':
217 return learn_move (FALSE);
218 case KEY_RIGHT:
219 case 'l':
220 return learn_move (TRUE);
221 case 'j':
222 dlg_one_down (learn_dlg);
223 return TRUE;
224 case 'k':
225 dlg_one_up (learn_dlg);
226 return TRUE;
229 /* Prevent from disappearing if a non-defined sequence is pressed
230 and contains a button hotkey. Only recognize hotkeys with ALT. */
231 return (c < 255 && g_ascii_isalnum (c));
234 /* --------------------------------------------------------------------------------------------- */
236 static cb_ret_t
237 learn_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
239 switch (msg)
241 case DLG_KEY:
242 return learn_check_key (parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
244 default:
245 return default_dlg_callback (h, sender, msg, parm, data);
249 /* --------------------------------------------------------------------------------------------- */
251 static void
252 init_learn (void)
254 const int dlg_width = 78;
255 const int dlg_height = 23;
257 struct
259 int ret_cmd, flags, y, x;
260 const char *text;
261 } learn_but[BUTTONS] =
263 /* *INDENT-OFF* */
264 {B_CANCEL, NORMAL_BUTTON, 0, 39, N_("&Cancel")},
265 {B_ENTER, DEFPUSH_BUTTON, 0, 25, N_("&Save")}
266 /* *INDENT-ON* */
269 int x, y, i, j;
270 const key_code_name_t *key;
271 char buffer[BUF_TINY];
273 #ifdef ENABLE_NLS
274 static gboolean i18n_flag = FALSE;
275 if (!i18n_flag)
277 learn_title = _(learn_title);
278 i18n_flag = TRUE;
281 learn_but[0].text = _(learn_but[0].text);
282 learn_but[0].x = dlg_width / 2 + 4;
284 learn_but[1].text = _(learn_but[1].text);
285 learn_but[1].x = dlg_width / 2 - (str_term_width1 (learn_but[1].text) + 9);
286 #endif /* ENABLE_NLS */
288 do_refresh ();
290 learn_dlg =
291 create_dlg (TRUE, 0, 0, dlg_height, dlg_width, dialog_colors, learn_callback, NULL,
292 "[Learn keys]", learn_title, DLG_CENTER | DLG_REVERSE);
294 for (i = 0; i < BUTTONS; i++)
295 add_widget (learn_dlg,
296 button_new (dlg_height - 3 + learn_but[i].y, learn_but[i].x,
297 learn_but[i].ret_cmd, learn_but[i].flags, _(learn_but[i].text), 0));
299 x = UX;
300 y = UY;
301 for (key = key_name_conv_tab, j = 0;
302 key->name != NULL && strcmp (key->name, "kpleft") != 0; key++, j++)
304 learnkeys = g_new (learnkey_t, j);
305 x += ((j - 1) / ROWS) * COLSHIFT;
306 y += (j - 1) % ROWS;
307 learn_total = j;
308 learnok = 0;
309 learnchanged = FALSE;
310 for (i = j - 1, key = key_name_conv_tab + j - 1; i >= 0; i--, key--)
312 learnkeys[i].ok = FALSE;
313 learnkeys[i].sequence = NULL;
314 g_snprintf (buffer, sizeof (buffer), "%-16s", _(key->longname));
315 learnkeys[i].button =
316 WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
317 add_widget (learn_dlg, learnkeys[i].button);
318 learnkeys[i].label = WIDGET (label_new (y, x + 19, ""));
319 add_widget (learn_dlg, learnkeys[i].label);
320 if (i % 13 != 0)
321 y--;
322 else
324 x -= COLSHIFT;
325 y = UY + ROWS - 1;
329 add_widget (learn_dlg,
330 label_new (UY + 14, 5,
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.")));
336 /* --------------------------------------------------------------------------------------------- */
338 static void
339 learn_done (void)
341 destroy_dlg (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 = run_dlg (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 /* --------------------------------------------------------------------------------------------- */