(init_learn): fix discard of "const" qualifier from pointer target type assignment.
[midnight-commander.git] / src / learn.c
blobca006f39dfe98563f396c5d134294f614ae2348b
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 /*** file scope type declarations ****************************************************************/
61 typedef struct
63 Widget *button;
64 Widget *label;
65 gboolean ok;
66 char *sequence;
67 } learnkey_t;
69 /*** file scope variables ************************************************************************/
71 static WDialog *learn_dlg;
72 static const char *learn_title = N_("Learn keys");
74 static learnkey_t *learnkeys = NULL;
75 static int learn_total;
76 static int learnok;
77 static gboolean learnchanged = FALSE;
79 /*** file scope functions ************************************************************************/
80 /* --------------------------------------------------------------------------------------------- */
82 static int
83 learn_button (WButton * button, int action)
85 WDialog *d;
86 char *seq;
88 (void) button;
90 d = create_message (D_ERROR, _("Teach me a key"),
91 _("Please press the %s\n"
92 "and then wait until this message disappears.\n\n"
93 "Then, press it again to see if OK appears\n"
94 "next to its button.\n\n"
95 "If you want to escape, press a single Escape key\n"
96 "and wait as well."), _(key_name_conv_tab[action - B_USER].longname));
97 mc_refresh ();
98 if (learnkeys[action - B_USER].sequence != NULL)
100 g_free (learnkeys[action - B_USER].sequence);
101 learnkeys[action - B_USER].sequence = NULL;
104 seq = learn_key ();
105 if (seq != NULL)
107 /* Esc hides the dialog and do not allow definitions of
108 * regular characters
110 gboolean seq_ok = FALSE;
112 if (*seq != '\0' && strcmp (seq, "\\e") != 0 && strcmp (seq, "\\e\\e") != 0
113 && strcmp (seq, "^m") != 0 && strcmp (seq, "^i") != 0
114 && (seq[1] != '\0' || *seq < ' ' || *seq > '~'))
116 learnchanged = TRUE;
117 learnkeys[action - B_USER].sequence = seq;
118 seq = convert_controls (seq);
119 seq_ok = define_sequence (key_name_conv_tab[action - B_USER].code, seq, MCKEY_NOACTION);
122 if (!seq_ok)
123 message (D_NORMAL, _("Cannot accept this key"), _("You have entered \"%s\""), seq);
125 g_free (seq);
128 dlg_run_done (d);
129 destroy_dlg (d);
131 dlg_select_widget (learnkeys[action - B_USER].button);
133 return 0; /* Do not kill learn_dlg */
136 /* --------------------------------------------------------------------------------------------- */
138 static gboolean
139 learn_move (gboolean right)
141 int i, totalcols;
143 totalcols = (learn_total - 1) / ROWS + 1;
144 for (i = 0; i < learn_total; i++)
145 if (learnkeys[i].button == WIDGET (learn_dlg->current->data))
147 if (right)
149 if (i < learn_total - ROWS)
150 i += ROWS;
151 else
152 i %= ROWS;
154 else
156 if (i / ROWS != 0)
157 i -= ROWS;
158 else if (i + (totalcols - 1) * ROWS >= learn_total)
159 i += (totalcols - 2) * ROWS;
160 else
161 i += (totalcols - 1) * ROWS;
163 dlg_select_widget (learnkeys[i].button);
164 return TRUE;
167 return FALSE;
170 /* --------------------------------------------------------------------------------------------- */
172 static gboolean
173 learn_check_key (int c)
175 int i;
177 for (i = 0; i < learn_total; i++)
179 if (key_name_conv_tab[i].code != c || learnkeys[i].ok)
180 continue;
182 dlg_select_widget (learnkeys[i].button);
183 /* TRANSLATORS: This label appears near learned keys. Keep it short. */
184 label_set_text (LABEL (learnkeys[i].label), _("OK"));
185 learnkeys[i].ok = TRUE;
186 learnok++;
187 if (learnok >= learn_total)
189 learn_dlg->ret_value = B_CANCEL;
190 if (learnchanged)
192 if (query_dialog (learn_title,
194 ("It seems that all your keys already\n"
195 "work fine. That's great."), D_ERROR, 2,
196 _("&Save"), _("&Discard")) == 0)
197 learn_dlg->ret_value = B_ENTER;
199 else
201 message (D_ERROR, learn_title,
203 ("Great! You have a complete terminal database!\n"
204 "All your keys work well."));
206 dlg_stop (learn_dlg);
208 return TRUE;
211 switch (c)
213 case KEY_LEFT:
214 case 'h':
215 return learn_move (FALSE);
216 case KEY_RIGHT:
217 case 'l':
218 return learn_move (TRUE);
219 case 'j':
220 dlg_one_down (learn_dlg);
221 return TRUE;
222 case 'k':
223 dlg_one_up (learn_dlg);
224 return TRUE;
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));
232 /* --------------------------------------------------------------------------------------------- */
234 static cb_ret_t
235 learn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
237 switch (msg)
239 case MSG_KEY:
240 return learn_check_key (parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
242 default:
243 return dlg_default_callback (w, sender, msg, parm, data);
247 /* --------------------------------------------------------------------------------------------- */
249 static void
250 init_learn (void)
252 const int dlg_width = 78;
253 const int dlg_height = 23;
255 /* buttons */
256 int bx0, bx1;
257 const char *b0 = N_("&Save");
258 const char *b1 = N_("&Cancel");
259 int bl0, bl1;
261 int x, y, i;
262 const key_code_name_t *key;
264 #ifdef ENABLE_NLS
265 static gboolean i18n_flag = FALSE;
266 if (!i18n_flag)
268 learn_title = _(learn_title);
269 i18n_flag = TRUE;
272 b0 = _(b0);
273 b1 = _(b1);
274 #endif /* ENABLE_NLS */
276 do_refresh ();
278 learn_dlg =
279 create_dlg (TRUE, 0, 0, dlg_height, dlg_width, dialog_colors, learn_callback, NULL,
280 "[Learn keys]", learn_title, DLG_CENTER);
282 /* find first unshown button */
283 for (key = key_name_conv_tab, learn_total = 0;
284 key->name != NULL && strcmp (key->name, "kpleft") != 0; key++, learn_total++)
287 learnok = 0;
288 learnchanged = FALSE;
290 learnkeys = g_new (learnkey_t, learn_total);
292 x = UX;
293 y = UY;
295 /* add buttons and "OK" labels */
296 for (i = 0; i < learn_total; i++)
298 char buffer[BUF_TINY];
299 const char *label;
300 int padding;
302 learnkeys[i].ok = FALSE;
303 learnkeys[i].sequence = NULL;
305 label = _(key_name_conv_tab[i].longname);
306 padding = 16 - str_term_width1 (label);
307 padding = max (0, padding);
308 g_snprintf (buffer, sizeof (buffer), "%s%*s", label, padding, "");
310 learnkeys[i].button =
311 WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
312 learnkeys[i].label = WIDGET (label_new (y, x + 19, ""));
313 add_widget (learn_dlg, learnkeys[i].button);
314 add_widget (learn_dlg, learnkeys[i].label);
316 y++;
317 if (y == UY + ROWS)
319 x += COLSHIFT;
320 y = UY;
324 add_widget (learn_dlg, hline_new (dlg_height - 8, -1, -1));
325 add_widget (learn_dlg,
326 label_new (dlg_height - 7, 5,
327 _("Press all the keys mentioned here. After you have done it, check\n"
328 "which keys are not marked with OK. Press space on the missing\n"
329 "key, or click with the mouse to define it. Move around with Tab.")));
330 add_widget (learn_dlg, hline_new (dlg_height - 4, -1, -1));
331 /* buttons */
332 bl0 = str_term_width1 (b0) + 5; /* default button */
333 bl1 = str_term_width1 (b1) + 3; /* normal button */
334 bx0 = (dlg_width - (bl0 + bl1 + 1)) / 2;
335 bx1 = bx0 + bl0 + 1;
336 add_widget (learn_dlg, button_new (dlg_height - 3, bx0, B_ENTER, DEFPUSH_BUTTON, b0, NULL));
337 add_widget (learn_dlg, button_new (dlg_height - 3, bx1, B_CANCEL, NORMAL_BUTTON, b1, NULL));
340 /* --------------------------------------------------------------------------------------------- */
342 static void
343 learn_done (void)
345 destroy_dlg (learn_dlg);
346 repaint_screen ();
349 /* --------------------------------------------------------------------------------------------- */
351 static void
352 learn_save (void)
354 int i;
355 char *section;
356 gboolean profile_changed = FALSE;
358 section = g_strconcat ("terminal:", getenv ("TERM"), (char *) NULL);
360 for (i = 0; i < learn_total; i++)
361 if (learnkeys[i].sequence != NULL)
363 char *esc_str;
365 esc_str = strutils_escape (learnkeys[i].sequence, -1, ";\\", TRUE);
366 mc_config_set_string_raw_value (mc_main_config, section, key_name_conv_tab[i].name,
367 esc_str);
368 g_free (esc_str);
370 profile_changed = TRUE;
373 /* On the one hand no good idea to save the complete setup but
374 * without 'Auto save setup' the new key-definitions will not be
375 * saved unless the user does an 'Options/Save Setup'.
376 * On the other hand a save-button that does not save anything to
377 * disk is much worse.
379 if (profile_changed)
380 mc_config_save_file (mc_main_config, NULL);
382 g_free (section);
385 /* --------------------------------------------------------------------------------------------- */
386 /*** public functions ****************************************************************************/
387 /* --------------------------------------------------------------------------------------------- */
389 void
390 learn_keys (void)
392 int save_old_esc_mode = old_esc_mode;
393 gboolean save_alternate_plus_minus = mc_global.tty.alternate_plus_minus;
394 int result;
396 /* old_esc_mode cannot work in learn keys dialog */
397 old_esc_mode = 0;
399 /* don't translate KP_ADD, KP_SUBTRACT and
400 KP_MULTIPLY to '+', '-' and '*' in
401 correct_key_code */
402 mc_global.tty.alternate_plus_minus = TRUE;
403 application_keypad_mode ();
405 init_learn ();
406 result = run_dlg (learn_dlg);
408 old_esc_mode = save_old_esc_mode;
409 mc_global.tty.alternate_plus_minus = save_alternate_plus_minus;
411 if (!mc_global.tty.alternate_plus_minus)
412 numeric_keypad_mode ();
414 if (result == B_ENTER)
415 learn_save ();
417 learn_done ();
420 /* --------------------------------------------------------------------------------------------- */