Add useful macros for widget type cast.
[midnight-commander.git] / src / learn.c
blob57d2ed69854e92028b14f6709c803a40ce742edf
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];
300 learnkeys[i].ok = FALSE;
301 learnkeys[i].sequence = NULL;
302 g_snprintf (buffer, sizeof (buffer), "%-16s", _(key_name_conv_tab[i].longname));
303 learnkeys[i].button =
304 WIDGET (button_new (y, x, B_USER + i, NARROW_BUTTON, buffer, learn_button));
305 learnkeys[i].label = WIDGET (label_new (y, x + 19, ""));
306 add_widget (learn_dlg, learnkeys[i].button);
307 add_widget (learn_dlg, learnkeys[i].label);
309 y++;
310 if (y == UY + ROWS)
312 x += COLSHIFT;
313 y = UY;
317 add_widget (learn_dlg, hline_new (dlg_height - 8, -1, -1));
318 add_widget (learn_dlg,
319 label_new (dlg_height - 7, 5,
320 _("Press all the keys mentioned here. After you have done it, check\n"
321 "which keys are not marked with OK. Press space on the missing\n"
322 "key, or click with the mouse to define it. Move around with Tab.")));
323 add_widget (learn_dlg, hline_new (dlg_height - 4, -1, -1));
324 /* buttons */
325 bl0 = str_term_width1 (b0) + 5; /* default button */
326 bl1 = str_term_width1 (b1) + 3; /* normal button */
327 bx0 = (dlg_width - (bl0 + bl1 + 1)) / 2;
328 bx1 = bx0 + bl0 + 1;
329 add_widget (learn_dlg, button_new (dlg_height - 3, bx0, B_ENTER, DEFPUSH_BUTTON, b0, NULL));
330 add_widget (learn_dlg, button_new (dlg_height - 3, bx1, B_CANCEL, NORMAL_BUTTON, b1, NULL));
333 /* --------------------------------------------------------------------------------------------- */
335 static void
336 learn_done (void)
338 destroy_dlg (learn_dlg);
339 repaint_screen ();
342 /* --------------------------------------------------------------------------------------------- */
344 static void
345 learn_save (void)
347 int i;
348 char *section;
349 gboolean profile_changed = FALSE;
351 section = g_strconcat ("terminal:", getenv ("TERM"), (char *) NULL);
353 for (i = 0; i < learn_total; i++)
354 if (learnkeys[i].sequence != NULL)
356 char *esc_str;
358 esc_str = strutils_escape (learnkeys[i].sequence, -1, ";\\", TRUE);
359 mc_config_set_string_raw_value (mc_main_config, section, key_name_conv_tab[i].name,
360 esc_str);
361 g_free (esc_str);
363 profile_changed = TRUE;
366 /* On the one hand no good idea to save the complete setup but
367 * without 'Auto save setup' the new key-definitions will not be
368 * saved unless the user does an 'Options/Save Setup'.
369 * On the other hand a save-button that does not save anything to
370 * disk is much worse.
372 if (profile_changed)
373 mc_config_save_file (mc_main_config, NULL);
375 g_free (section);
378 /* --------------------------------------------------------------------------------------------- */
379 /*** public functions ****************************************************************************/
380 /* --------------------------------------------------------------------------------------------- */
382 void
383 learn_keys (void)
385 int save_old_esc_mode = old_esc_mode;
386 gboolean save_alternate_plus_minus = mc_global.tty.alternate_plus_minus;
387 int result;
389 /* old_esc_mode cannot work in learn keys dialog */
390 old_esc_mode = 0;
392 /* don't translate KP_ADD, KP_SUBTRACT and
393 KP_MULTIPLY to '+', '-' and '*' in
394 correct_key_code */
395 mc_global.tty.alternate_plus_minus = TRUE;
396 application_keypad_mode ();
398 init_learn ();
399 result = run_dlg (learn_dlg);
401 old_esc_mode = save_old_esc_mode;
402 mc_global.tty.alternate_plus_minus = save_alternate_plus_minus;
404 if (!mc_global.tty.alternate_plus_minus)
405 numeric_keypad_mode ();
407 if (result == B_ENTER)
408 learn_save ();
410 learn_done ();
413 /* --------------------------------------------------------------------------------------------- */