1 /**************************************************************************/
2 /* Klavaro - a flexible touch typing tutor */
3 /* Copyright (C) from 2005 until 2008 Felipe Castro */
4 /* Copyright (C) from 2009 until 2019 The Free Software Foundation */
5 /* */
6 /* This program is free software, licensed under the terms of the GNU */
7 /* General Public License as published by the Free Software Foundation, */
8 /* either version 3 of the License, or (at your option) any later */
9 /* version. You should have received a copy of the GNU General Public */
10 /* License along with this program. If not, */
11 /* see <https://www.gnu.org/licenses/>. */
12 /**************************************************************************/
13
14 /* Functions to implement and manage the keyboard editing operations
15 */
16 #include <errno.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <glib.h>
21 #include <glib/gstdio.h>
22 #include <gtk/gtk.h>
23
24 #include "auxiliar.h"
25 #include "main.h"
26 #include "callbacks.h"
27 #include "translation.h"
28 #include "keyboard.h"
29
30 #define KEY_DX 35
31 #define KEY_DY 35
32
33 extern gchar *KEYB_CUSTOM;
34 extern gchar *KEYB_EDIT;
35
36 //static GtkCssProvider *keyb_css = NULL;
37 GtkCssProvider *keyb_css = NULL;
38
39 static struct
40 {
41 gchar *name;
42 gchar *name_last;
43 gboolean modified_status;
44 gunichar lochars[4][KEY_LINE_LEN + 1];
45 gunichar upchars[4][KEY_LINE_LEN + 1];
46 GtkWidget *but[4][KEY_LINE_LEN - 1];
47 GtkWidget *lab[4][KEY_LINE_LEN - 1];
48 GtkWidget *entry;
49 struct
50 {
51 guint i;
52 guint j;
53 } pos;
54 gint cmb_n;
55 gint intro_step;
56 } keyb;
57 static guint x0[4] = {0, 49, 59, 43};
58
59 static struct
60 {
61 KeybLayout *orig; // Original layouts already defined
62 gint n_orig;
63 KeybLayout *cust; // Custom layouts created by the user
64 gint n_cust;
65 } layouts;
66
67 /* Constants
68 */
69 const gunichar vowels[] = {
70 L'a', L'e', L'i', L'o', L'u',
71 (gunichar) 945,
72 (gunichar) 949,
73 (gunichar) 953,
74 (gunichar) 959,
75 (gunichar) 965,
76 (gunichar) 1072,
77 (gunichar) 1077,
78 (gunichar) 1080,
79 (gunichar) 1086,
80 (gunichar) 1091,
81 // Tibetan vowels
82 (gunichar) 0x0F72,
83 (gunichar) 0x0F74,
84 (gunichar) 0x0F7A,
85 (gunichar) 0x0F7C,
86 L'\0'
87 };
88
89 /* Diacritic chars should never appear one after another
90 */
91 const gunichar diacritics[] = {
92 // Urdu diacritics
93 (gunichar) 0x0640,
94 (gunichar) 0x064B,
95 (gunichar) 0x064E,
96 (gunichar) 0x064F,
97 (gunichar) 0x0650,
98 (gunichar) 0x0651,
99 (gunichar) 0x0654,
100 (gunichar) 0x0670,
101 // Tibetan diacritics
102 (gunichar) 0x0F35,
103 (gunichar) 0x0F37,
104 (gunichar) 0x0F71,
105 (gunichar) 0x0F72,
106 (gunichar) 0x0F74,
107 (gunichar) 0x0F7A,
108 (gunichar) 0x0F7B,
109 (gunichar) 0x0F7C,
110 (gunichar) 0x0F7D,
111 (gunichar) 0x0F7E,
112 (gunichar) 0x0F80,
113 (gunichar) 0x0F83,
114 (gunichar) 0x0F90,
115 (gunichar) 0x0F91,
116 (gunichar) 0x0F92,
117 (gunichar) 0x0F94,
118 (gunichar) 0x0F95,
119 (gunichar) 0x0F96,
120 (gunichar) 0x0F97,
121 (gunichar) 0x0F98,
122 (gunichar) 0x0F99,
123 (gunichar) 0x0F9A,
124 (gunichar) 0x0F9F,
125 (gunichar) 0x0FA0,
126 (gunichar) 0x0FA1,
127 (gunichar) 0x0FA3,
128 (gunichar) 0x0FA4,
129 (gunichar) 0x0FA5,
130 (gunichar) 0x0FA6,
131 (gunichar) 0x0FA8,
132 (gunichar) 0x0FA9,
133 (gunichar) 0x0FAA,
134 (gunichar) 0x0FAB,
135 (gunichar) 0x0FAD,
136 (gunichar) 0x0FAE,
137 (gunichar) 0x0FAF,
138 (gunichar) 0x0FB0,
139 (gunichar) 0x0FB1,
140 (gunichar) 0x0FB2,
141 (gunichar) 0x0FB3,
142 (gunichar) 0x0FB4,
143 (gunichar) 0x0FB6,
144 (gunichar) 0x0FB7,
145 (gunichar) 0x0FB8,
146 (gunichar) 0x0FBA,
147 (gunichar) 0x0FBB,
148 // Other arabic diacritics
149 (gunichar) 0x0610,
150 (gunichar) 0x0611,
151 (gunichar) 0x0612,
152 (gunichar) 0x0613,
153 (gunichar) 0x0614,
154 (gunichar) 0x0615,
155 (gunichar) 0x0616,
156 (gunichar) 0x0617,
157 (gunichar) 0x0618,
158 (gunichar) 0x0619,
159 (gunichar) 0x061A,
160 (gunichar) 0x064C,
161 (gunichar) 0x064D,
162 (gunichar) 0x0652,
163 (gunichar) 0x0653,
164 (gunichar) 0x0655,
165 (gunichar) 0x0656,
166 (gunichar) 0x0657,
167 (gunichar) 0x0658,
168 (gunichar) 0x0659,
169 (gunichar) 0x065A,
170 (gunichar) 0x065B,
171 (gunichar) 0x065C,
172 (gunichar) 0x065D,
173 (gunichar) 0x065E,
174 (gunichar) 0x06D6,
175 (gunichar) 0x06D7,
176 (gunichar) 0x06D8,
177 (gunichar) 0x06D9,
178 (gunichar) 0x06DA,
179 (gunichar) 0x06DB,
180 (gunichar) 0x06DC,
181 (gunichar) 0x06DF,
182 (gunichar) 0x06E0,
183 (gunichar) 0x06E1,
184 (gunichar) 0x06E2,
185 (gunichar) 0x06E3,
186 (gunichar) 0x06E4,
187 (gunichar) 0x06E7,
188 (gunichar) 0x06E8,
189 (gunichar) 0x06EA,
190 (gunichar) 0x06EB,
191 (gunichar) 0x06EC,
192 (gunichar) 0x06ED,
193 L'\0'
194 };
195
196 /*******************************************************************************
197 * Interface functions
198 */
199 gchar *
keyb_get_name()200 keyb_get_name ()
201 {
202 return (keyb.name);
203 }
204
205 gchar *
keyb_get_name_last()206 keyb_get_name_last ()
207 {
208 return (keyb.name_last);
209 }
210
211 void
keyb_set_name(const gchar * name)212 keyb_set_name (const gchar * name)
213 {
214 g_free (keyb.name_last);
215 keyb.name_last = g_strdup (keyb.name);
216 g_free (keyb.name);
217 keyb.name = g_strdup (name);
218 }
219
220 void
keyb_init_name(const gchar * name)221 keyb_init_name (const gchar * name)
222 {
223 keyb.name = g_strdup (name);
224 keyb.name_last = g_strdup (name);
225 }
226
227 gunichar
keyb_get_lochars(gint i,gint j)228 keyb_get_lochars (gint i, gint j)
229 {
230 return (keyb.lochars[i][j]);
231 }
232
233 gunichar
keyb_get_upchars(gint i,gint j)234 keyb_get_upchars (gint i, gint j)
235 {
236 return (keyb.upchars[i][j]);
237 }
238
239 gboolean
keyb_get_modified_status()240 keyb_get_modified_status ()
241 {
242 return (keyb.modified_status);
243 }
244
245 void
keyb_set_modified_status(gboolean new_status)246 keyb_set_modified_status (gboolean new_status)
247 {
248 gtk_widget_set_sensitive (get_wg ("combobox_keyboard_country"), ! new_status);
249 gtk_widget_set_sensitive (get_wg ("combobox_keyboard_variant"), ! new_status);
250 gtk_widget_set_sensitive (get_wg ("button_kb_save"), new_status);
251 keyb.modified_status = new_status;
252 if (new_status)
253 {
254 callbacks_shield_set (TRUE);
255 gtk_combo_box_set_active (GTK_COMBO_BOX (get_wg ("combobox_keyboard_country")), 0);
256 gtk_combo_box_set_active (GTK_COMBO_BOX (get_wg ("combobox_keyboard_variant")), -1);
257 callbacks_shield_set (FALSE);
258 }
259 }
260
261 void
keyb_create_virtual_keys()262 keyb_create_virtual_keys ()
263 {
264 gint i, j;
265 gchar *hlp;
266 GtkFixed *fix;
267 GdkRGBA color;
268 gchar *css_text;
269 gchar *tmp;
270 gchar *tmp2;
271 gchar chcd;
272 GtkStyleContext *sc;
273
274 /* Set color of keys
275 */
276 if (main_preferences_exist ("colors", "key_fg"))
277 hlp = main_preferences_get_string ("colors", "key_fg");
278 else
279 hlp = g_strdup (KEYB_BLACK);
280 if (keyb_css == NULL)
281 {
282 keyb_css = gtk_css_provider_new ();
283 css_text = g_strdup ("");
284 for (chcd = '1'; chcd <= '9'; chcd++)
285 {
286 tmp = g_strdup_printf (".key-but%c {background-image: none; background-color: %s; color: %s} "
287 ".key-but%c:hover {background-image: none; background-color: white; color: black} "
288 ".key-but%c:active {background-image: none; background-color: black; color: white} ",
289 chcd, hints_color_from_charcode (chcd), hlp, chcd, chcd);
290 css_text = g_strdup_printf ("%s%s", css_text, tmp);
291 g_free (tmp);
292 }
293 gtk_css_provider_load_from_data (keyb_css, css_text, -1, NULL);
294 g_free (css_text);
295 }
296 g_free (hlp);
297
298 /* Set space key
299 */
300 sc = gtk_widget_get_style_context (get_wg ("but_space"));
301 gtk_style_context_add_provider (sc, GTK_STYLE_PROVIDER (keyb_css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
302 gtk_style_context_add_class (sc, "key-but5");
303
304 /* Create and position buttons and labels
305 */
306 fix = GTK_FIXED (get_wg ("fixed_keyboard"));
307 for (i = 0; i < 4; i++)
308 {
309 for (j = 0; j < KEY_LINE_LEN - 1; j++)
310 {
311 keyb.but[i][j] = gtk_button_new ();
312 gtk_fixed_put (fix, keyb.but[i][j], x0[i] + j * KEY_DX, i * KEY_DY);
313 gtk_widget_set_size_request (keyb.but[i][j], 32, 32);
314 g_signal_connect_after ((gpointer) keyb.but[i][j], "clicked",
315 G_CALLBACK (on_virtual_key_clicked), NULL);
316 g_signal_connect_after ((gpointer) keyb.but[i][j], "grab-focus",
317 G_CALLBACK (on_virtual_key_grab_focus), NULL);
318 keyb.lab[i][j] = gtk_label_new ("0");
319 gtk_container_add (GTK_CONTAINER (keyb.but[i][j]), keyb.lab[i][j]);
320
321 if (i > 0)
322 {
323 if (i == 1)
324 {
325 if (j > 12)
326 continue;
327 }
328 else
329 {
330 if (j > 11)
331 continue;
332 }
333 }
334 gtk_widget_show (keyb.but[i][j]);
335 gtk_widget_show (keyb.lab[i][j]);
336 }
337 }
338 gtk_widget_set_size_request (keyb.but[1][12], 53, 32);
339
340 /* Key entry little evil
341 */
342 keyb.entry = gtk_entry_new ();
343 gtk_fixed_put (fix, keyb.entry, 2, 2);
344 gtk_entry_set_width_chars (GTK_ENTRY (keyb.entry), 1);
345 gtk_entry_set_max_length (GTK_ENTRY (keyb.entry), 1);
346 gtk_entry_set_alignment (GTK_ENTRY (keyb.entry), 0.5);
347 gtk_widget_set_size_request (keyb.entry, 28, 28);
348 g_object_set (G_OBJECT (keyb.entry), "shadow-type", GTK_SHADOW_NONE, NULL);
349 g_signal_connect_after ((gpointer) keyb.entry, "changed", G_CALLBACK (on_virtual_key_changed), NULL);
350 }
351
352 /**********************************************************************
353 * Read the character sets (keyb.lochars[] & keyb.upchars[])
354 * for the keyboard currently selected.
355 */
356 void
keyb_set_chars()357 keyb_set_chars ()
358 {
359 gint i;
360 gchar *tmp_name = NULL;
361 gchar tmp_str[6 * KEY_LINE_LEN + 1];
362 glong n_itens;
363 gunichar *uchs;
364 FILE *fh;
365
366 /* Search at home
367 */
368 tmp_name = g_strconcat (main_path_user (), G_DIR_SEPARATOR_S, keyb.name, ".kbd", NULL);
369 fh = (FILE *) g_fopen (tmp_name, "r");
370 if (fh == NULL)
371 {
372 /* Search at data
373 */
374 g_free (tmp_name);
375 tmp_name = g_strconcat (main_path_data (), G_DIR_SEPARATOR_S, keyb.name, ".kbd", NULL);
376 fh = (FILE *) g_fopen (tmp_name, "r");
377 }
378 g_free (tmp_name);
379
380 /* Success */
381 if (fh)
382 {
383 for (i = 0; i < 4; i++)
384 {
385 tmp_name = fgets (tmp_str, 6 * KEY_LINE_LEN + 1, fh);
386 tmp_str[6 * KEY_LINE_LEN] = '\0';
387 uchs = g_utf8_to_ucs4_fast (tmp_str, -1, &n_itens);
388 if (n_itens > KEY_LINE_LEN)
389 g_error ("invalid keyboard layout: %s\n"
390 "invalid line: %i\n"
391 "invalid number of chars: %li", keyb.name, i + 1, n_itens);
392 memcpy (keyb.lochars[i], uchs, (n_itens - 1) * sizeof (gunichar));
393 g_free (uchs);
394 for (; n_itens < KEY_LINE_LEN; n_itens++)
395 keyb.lochars[i][n_itens] = L' ';
396 }
397 for (i = 0; i < 4; i++)
398 {
399 tmp_name = fgets (tmp_str, 6 * KEY_LINE_LEN + 1, fh);
400 tmp_str[6 * KEY_LINE_LEN] = '\0';
401 uchs = g_utf8_to_ucs4_fast (tmp_str, -1, &n_itens);
402 if (n_itens > KEY_LINE_LEN)
403 g_error ("invalid keyboard layout: %s\n"
404 "invalid line: %i\n"
405 "invalid number of chars: %li", keyb.name, i + 5, n_itens);
406 memcpy (keyb.upchars[i], uchs, (n_itens - 1) * sizeof (gunichar));
407 g_free (uchs);
408 for (; n_itens < KEY_LINE_LEN; n_itens++)
409 keyb.upchars[i][n_itens] = L' ';
410 }
411 fclose (fh);
412
413 keyb_set_modified_status (FALSE);
414 }
415 /*
416 * Recursively try defaults
417 */
418 else
419 {
420 if (g_str_equal (keyb.name, trans_get_default_keyboard ()))
421 {
422 main_preferences_remove ("tutor", "keyboard");
423 g_error ("couldn't open the default keyboard layout: [%s]", trans_get_default_keyboard ());
424 }
425
426 g_message ("couldn't find the keyboard layout: \"%s\"\n"
427 " Opening the default one: \"%s\"", keyb.name, trans_get_default_keyboard ());
428 main_preferences_set_string ("tutor", "keyboard", trans_get_default_keyboard());
429 keyb_set_name (trans_get_default_keyboard ());
430 keyb_set_chars ();
431 return;
432 }
433 }
434
435 /**********************************************************************
436 * Test if chr belongs to the current key set
437 */
keyb_is_inset(gunichar chr)438 gboolean keyb_is_inset (gunichar chr)
439 {
440 register gint i, j;
441
442 for (i = 0; i < 4; i++)
443 for (j = 0; j <= KEY_LINE_LEN; j++)
444 if (chr == keyb.lochars[i][j])
445 return (TRUE);
446
447 for (i = 0; i < 4; i++)
448 for (j = 0; j <= KEY_LINE_LEN; j++)
449 if (chr == keyb.upchars[i][j])
450 return (TRUE);
451 return (FALSE);
452 }
453
454 /**********************************************************************
455 * Test if chr is a vowel
456 */
457 gboolean
keyb_is_vowel(gunichar chr)458 keyb_is_vowel (gunichar chr)
459 {
460 gint i;
461
462 for (i = 0; vowels[i] != L'\0'; i++)
463 if (g_unichar_tolower (chr) == vowels[i])
464 return (TRUE);
465 return (FALSE);
466 }
467
468 /**********************************************************************
469 * Test if chr is a diacritic character
470 */
471 gboolean
keyb_is_diacritic(gunichar chr)472 keyb_is_diacritic (gunichar chr)
473 {
474 gint i;
475 gunichar *diac_array;
476
477 for (i = 0; diacritics[i] != L'\0'; i++)
478 if (chr == diacritics[i])
479 return (TRUE);
480 return (FALSE);
481 }
482
483 /**********************************************************************
484 * Get the set of available vowels of the keyboard
485 */
486 gint
keyb_get_vowels(gunichar * vows)487 keyb_get_vowels (gunichar * vows)
488 {
489 gint i;
490 gint j;
491 gint k = 0;
492
493 for (i = 0; i < 4; i++)
494 for (j = 0; j < KEY_LINE_LEN; j++)
495 {
496 if (keyb_is_vowel (keyb.lochars[i][j]))
497 vows[k++] = keyb.lochars[i][j];
498 if (k == 20)
499 break;
500 }
501 if (k == 0)
502 for (i = j = 0, k = 5; i < 5 && j < 10; i++, j++)
503 {
504 for (; keyb_is_diacritic (keyb.lochars[2][j]) && j < 12; j++);
505 vows[i] = keyb.lochars[2][j];
506 }
507 return (k);
508 }
509
510 /**********************************************************************
511 * Get the set of available consonants of the keyboard
512 */
513 gint
keyb_get_consonants(gunichar * consonants)514 keyb_get_consonants (gunichar * consonants)
515 {
516 gint i, j;
517 gint k = 0;
518 gunichar chr;
519
520 for (i = 0; i < 4; i++)
521 for (j = 0; j < KEY_LINE_LEN; j++)
522 {
523 chr = keyb.lochars[i][j];
524 if (g_unichar_isalpha (chr) && (!keyb_is_vowel (chr)))
525 consonants[k++] = chr;
526
527 chr = g_unichar_tolower (keyb.upchars[i][j]);
528 if (g_unichar_isalpha (chr) && (!keyb_is_vowel (chr))
529 && (chr != keyb.lochars[i][j]))
530 consonants[k++] = chr;
531 }
532 return (k);
533 }
534
535 /**********************************************************************
536 * Get the set of available symbols of the keyboard
537 */
538 gint
keyb_get_symbols(gunichar * symbols)539 keyb_get_symbols (gunichar * symbols)
540 {
541 gint i, j;
542 gint k = 0;
543 gunichar chr;
544
545 for (i = 0; i < 4; i++)
546 for (j = 0; j < KEY_LINE_LEN; j++)
547 {
548 chr = keyb.lochars[i][j];
549 if (g_unichar_ispunct (chr))
550 symbols[k++] = chr;
551
552 chr = keyb.upchars[i][j];
553 if (g_unichar_ispunct (chr))
554 symbols[k++] = chr;
555 }
556 return (k);
557 }
558
559 /**********************************************************************
560 * Get the set of available non-arabic digits in the keyboard
561 */
562 gint
keyb_get_altnums(gunichar * altnums)563 keyb_get_altnums (gunichar * altnums)
564 {
565 gint i, j;
566 gint k = 0;
567 gunichar chr;
568
569 for (i = 0; i < 4; i++)
570 for (j = 0; j < KEY_LINE_LEN; j++)
571 {
572 chr = keyb.lochars[i][j];
573 if (g_unichar_isdigit (chr) && chr > 255)
574 altnums[k++] = chr;
575
576 chr = keyb.upchars[i][j];
577 if (g_unichar_isdigit (chr) && chr > 255)
578 altnums[k++] = chr;
579 }
580 return (k);
581 }
582
583 /**********************************************************************
584 * Get the upper case of a letter, only if it's included in the keyboard (by shift)
585 */
586 gunichar
keyb_unichar_toupper(gunichar uchar)587 keyb_unichar_toupper (gunichar uchar)
588 {
589 gint i,j;
590 gunichar Uchar;
591
592 Uchar = g_unichar_toupper (uchar);
593 for (i = 0; i < 4; i++)
594 for (j = 0; j < KEY_LINE_LEN; j++)
595 if (uchar == keyb.lochars[i][j] && Uchar == keyb.upchars[i][j])
596 return Uchar;
597 return uchar;
598 }
599
600 /**********************************************************************
601 * Save the custom keyboard layout created by the user
602 */
603 void
keyb_save_new_layout()604 keyb_save_new_layout ()
605 {
606 gint i;
607 gchar *tmp_name = NULL;
608 FILE *fh;
609
610 assert_user_dir ();
611 tmp_name = g_strconcat (main_path_user (), G_DIR_SEPARATOR_S, keyb.name, ".kbd", NULL);
612 fh = (FILE *) g_fopen (tmp_name, "w");
613 g_free (tmp_name);
614
615 for (i = 0; i < 4; i++)
616 {
617 tmp_name = g_ucs4_to_utf8 (keyb.lochars[i], KEY_LINE_LEN - 1, NULL, NULL, NULL);
618 fprintf (fh, "%s\n", tmp_name);
619 g_free (tmp_name);
620 }
621 for (i = 0; i < 4; i++)
622 {
623 tmp_name = g_ucs4_to_utf8 (keyb.upchars[i], KEY_LINE_LEN - 1, NULL, NULL, NULL);
624 fprintf (fh, "%s\n", tmp_name);
625 g_free (tmp_name);
626 }
627 fclose (fh);
628
629 keyb_set_modified_status (FALSE);
630 }
631
632 /**********************************************************************
633 * Remove custom keyboard layout created by the user
634 */
635 void
keyb_remove_user_layout()636 keyb_remove_user_layout ()
637 {
638 guint active;
639 gchar *aux;
640 gchar *tmp_name;
641 GtkComboBox *cmb;
642
643 callbacks_shield_set (TRUE);
644
645 cmb = GTK_COMBO_BOX (get_wg ("combobox_keyboard_variant"));
646 aux = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cmb));
647 active = gtk_combo_box_get_active (cmb);
648 gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (cmb), active);
649
650 tmp_name = g_strconcat (main_path_user (), G_DIR_SEPARATOR_S, aux, ".kbd", NULL);
651 g_unlink (tmp_name);
652 g_free (tmp_name);
653
654 keyb_set_keyboard_layouts ();
655
656 gtk_combo_box_set_active (cmb, -1);
657
658 callbacks_shield_set (FALSE);
659 }
660
661
662 /**********************************************************************
663 * Update the virtual keyboard accordingly to its character set and
664 * shift key state.
665 */
666 void
keyb_update_virtual_layout()667 keyb_update_virtual_layout ()
668 {
669 gint i, j;
670 gchar ut8[7];
671 gunichar uch;
672 gboolean tog_state;
673 GtkWidget *wg;
674
675 wg = get_wg ("toggle_shift1");
676 tog_state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wg));
677 for (i = 0; i < 4; i++)
678 {
679 for (j = 0; j < KEY_LINE_LEN - (i == 0 ? 1 : (i == 1 ? 2 : 3)); j++)
680 {
681 uch = tog_state ? keyb.upchars[i][j] : keyb.lochars[i][j];
682 if (g_unichar_isalpha (uch)
683 && g_unichar_tolower (keyb.upchars[i][j]) == keyb.lochars[i][j])
684 uch = g_unichar_toupper (uch);
685 ut8[g_unichar_to_utf8 (uch, ut8)] = '\0';
686 gtk_label_set_text (GTK_LABEL (keyb.lab[i][j]), ut8);
687 }
688 }
689 }
690
691 /* Get a list of .kbd file names in the subdir path, stripping their .kbd extensions
692 */
693 GList *
keyb_get_layout_list_from_path(gchar * path)694 keyb_get_layout_list_from_path (gchar *path)
695 {
696 gsize name_len;
697 GDir *dir = NULL;
698 gchar *dentry = NULL;
699 GList *files = NULL;
700
701 dir = g_dir_open (path, 0, NULL);
702 if (dir == NULL)
703 g_error ("keyb_get_layout_list_from_path ():\n\tCould not find this directory:\n\t%s\n", path);
704
705 while ( (dentry = g_strdup (g_dir_read_name (dir))) )
706 {
707 name_len = strlen (dentry);
708 if (name_len > 255 || name_len < 5)
709 {
710 g_free (dentry);
711 continue;
712 }
713
714 if (! g_str_has_suffix (dentry, ".kbd"))
715 {
716 g_free (dentry);
717 continue;
718 }
719
720 dentry[name_len - 4] = '\0';
721 if (g_str_equal (dentry, ".tmp"))
722 {
723 g_free (dentry);
724 continue;
725 }
726 files = g_list_insert_sorted (files, dentry, compare_string_function);
727 }
728 g_dir_close (dir);
729
730 return (files);
731 }
732
733 /* Get the country code from a keyboard name
734 */
735 gchar *
keyb_get_country_code(const gchar * kbd)736 keyb_get_country_code (const gchar *kbd)
737 {
738 gchar *code = NULL;
739
740 code = strchr (kbd, '_');
741 if (code)
742 code = strdup (code + 1);
743 else
744 code = strdup ("xx");
745 code[2] = '\0';
746
747 return code;
748 }
749
750
751 /* Get the country from a keyboard name
752 */
753 gchar *
keyb_get_country(const gchar * kbd)754 keyb_get_country (const gchar *kbd)
755 {
756 gchar *country = NULL;
757 gchar *code = NULL;
758
759 code = keyb_get_country_code (kbd);
760 country = g_strdup (trans_code_to_country (code));
761 g_free (code);
762
763 return country;
764 }
765
766 /* Get the variant from a keyboard name
767 */
768 gchar *
keyb_get_variant(const gchar * kbd)769 keyb_get_variant (const gchar *kbd)
770 {
771 gchar *begin;
772 gchar *end;
773
774 begin = g_strdup (kbd);
775 end = strchr (begin, '_');
776 if (end == NULL)
777 return begin;
778 *end = '\0';
779 end++;
780 end = strchr (end, '_');
781 if (end == NULL)
782 return begin;
783 end = g_strconcat (begin, end, NULL);
784 g_free (begin);
785
786 return end;
787 }
788
789 /* Set the array of available keyboard layouts
790 */
791 #define LAYOUT_BLOCK 64
792 void
keyb_set_keyboard_layouts()793 keyb_set_keyboard_layouts ()
794 {
795 static gboolean init = FALSE;
796 gchar *data;
797 gint i;
798 GList *files;
799
800 if (! init)
801 {
802
803 /* Read original layouts just once, now.
804 */
805 files = keyb_get_layout_list_from_path (main_path_data ());
806 layouts.n_orig = g_list_length (files);
807 layouts.orig = g_malloc (layouts.n_orig * sizeof (KeybLayout));
808 //g_printf ("==> Data dir: %s\n", main_path_data ());
809 for (i = 0; i < layouts.n_orig; i++)
810 {
811 data = g_list_nth_data (files, i);
812 //g_printf ("kb(%i): %s\n", i, data);
813 layouts.orig[i].name = data;
814 layouts.orig[i].country = keyb_get_country (data);
815 layouts.orig[i].variant = keyb_get_variant (data);
816 //g_printf ("kb(%i): %s\t", i, layouts.orig[i].name);
817 //g_printf ("%s\t", layouts.orig[i].country);
818 //g_printf ("%s\n", layouts.orig[i].variant);
819 }
820 g_list_free (files);
821
822 init = TRUE;
823 layouts.n_cust = 0;
824 layouts.cust = g_malloc (LAYOUT_BLOCK * sizeof (KeybLayout));
825 }
826
827 /*
828 * Reads the list of custom files
829 */
830 for (i = 0; i < layouts.n_cust; i++)
831 g_free (layouts.cust[i].name);
832 assert_user_dir ();
833 files = keyb_get_layout_list_from_path (main_path_user ());
834 layouts.n_cust = g_list_length (files);
835 if (layouts.n_cust == 0)
836 return;
837 if (layouts.n_cust > LAYOUT_BLOCK)
838 layouts.cust = g_realloc (layouts.cust, layouts.n_cust * sizeof (KeybLayout));
839 for (i = 0; i < layouts.n_cust; i++)
840 {
841 data = g_list_nth_data (files, i);
842 layouts.cust[i].name = data;
843 //g_printf ("kb(%i): %s\n", i, layouts.cust[i].name);
844 }
845 g_list_free (files);
846 }
847
848 void
keyb_update_from_variant(gchar * cmb_country,gchar * cmb_variant)849 keyb_update_from_variant (gchar *cmb_country, gchar *cmb_variant)
850 {
851 gint i;
852 gchar *country;
853 gchar *variant;
854 GtkComboBox *cmb;
855
856 cmb = GTK_COMBO_BOX (get_wg (cmb_country));
857 country = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cmb));
858 if (country == NULL)
859 return;
860
861 cmb = GTK_COMBO_BOX (get_wg (cmb_variant));
862 variant = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cmb));
863 if (variant == NULL)
864 {
865 g_free (country);
866 return;
867 }
868
869 callbacks_shield_set (TRUE);
870
871 if (g_str_equal (country, KEYB_CUSTOM))
872 {
873 /* Update the keyboard for a custom layout
874 */
875 if (! g_str_equal (variant, KEYB_EDIT))
876 {
877 keyb_set_name (variant);
878 keyb_set_chars ();
879 keyb_update_virtual_layout ();
880 }
881 }
882 else
883 {
884 /* Update it for a original layout
885 */
886 for (i = 0; i < layouts.n_orig; i++)
887 {
888 if (g_str_equal (layouts.orig[i].country, country))
889 if (g_str_equal (layouts.orig[i].variant, variant))
890 break;
891 }
892
893 if (i == layouts.n_orig)
894 g_warning ("selected unavailable keyboard layout.");
895 else
896 {
897 keyb_set_name (layouts.orig[i].name);
898 keyb_set_chars ();
899 keyb_update_virtual_layout ();
900 }
901 }
902
903 g_free (country);
904 g_free (variant);
905
906 callbacks_shield_set (FALSE);
907 }
908
909 void
keyb_set_combo_kbd_variant(gchar * cmb_country,gchar * cmb_variant)910 keyb_set_combo_kbd_variant (gchar *cmb_country, gchar *cmb_variant)
911 {
912 gint i;
913 gint n;
914 gchar *country_txt;
915 gboolean valid;
916 GtkComboBox *cmb;
917 GtkTreeModel *tmd;
918 GtkTreeIter iter;
919
920 callbacks_shield_set (TRUE);
921
922 /* Clear the combo variant
923 */
924 cmb = GTK_COMBO_BOX (get_wg (cmb_variant));
925 tmd = gtk_combo_box_get_model (cmb);
926 n = 0;
927 valid = gtk_tree_model_get_iter_first (tmd, &iter);
928 while (valid)
929 {
930 n++;
931 valid = gtk_tree_model_iter_next (tmd, &iter);
932 }
933 for (i = 0; i < n; i++)
934 gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (cmb), 0);
935
936 /* Get the selected country text
937 */
938 country_txt = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (get_wg (cmb_country)));
939 if (country_txt == NULL)
940 {
941 g_warning ("Country combo not set, so nothing done with variant combo.");
942 callbacks_shield_set (FALSE);
943 return;
944 }
945
946 /* Set the original variants for the selected country */
947 if (! g_str_equal (country_txt, KEYB_CUSTOM))
948 {
949 gchar *current;
950
951 n = 0;
952 for (i = 0; i < layouts.n_orig; i++)
953 {
954 if (g_str_equal (layouts.orig[i].country, country_txt))
955 {
956 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cmb), layouts.orig[i].variant);
957 n++;
958 }
959 }
960
961 current = keyb_get_variant (keyb.name);
962 for (i = 0; i < n; i++)
963 {
964 gchar *variant;
965
966 gtk_combo_box_set_active (cmb, i);
967 variant = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cmb));
968 if (g_str_equal (variant, current))
969 {
970 g_free (variant);
971 break;
972 }
973 g_free (variant);
974 }
975
976 if (i == n)
977 {
978 if (n > 0)
979 gtk_combo_box_set_active (cmb, 0);
980 else
981 gtk_combo_box_set_active (cmb, -1);
982 }
983
984 if (n > 1)
985 gtk_widget_set_sensitive (get_wg (cmb_variant), TRUE);
986 else
987 gtk_widget_set_sensitive (get_wg (cmb_variant), FALSE);
988
989 g_free (current);
990
991 }
992 /* Set custom layouts in the variant combo */
993 else
994 {
995 n = 0;
996 if (g_str_equal (cmb_variant, "combobox_kbd_variant"))
997 {
998 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cmb), KEYB_EDIT);
999 n++;
1000 }
1001 for (i = 0; i < layouts.n_cust; i++)
1002 {
1003 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cmb), layouts.cust[i].name);
1004 n++;
1005 }
1006
1007 for (i = 0; i < n; i++)
1008 {
1009 gchar *variant;
1010
1011 gtk_combo_box_set_active (cmb, i);
1012 variant = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cmb));
1013 if (g_str_equal (variant, keyb.name))
1014 {
1015 g_free (variant);
1016 break;
1017 }
1018 g_free (variant);
1019 }
1020
1021 if (i == n)
1022 {
1023 if (n > 1)
1024 gtk_combo_box_set_active (cmb, 1);
1025 else if (! gtk_widget_get_visible (get_wg ("window_keyboard")))
1026 {
1027 gtk_combo_box_set_active (cmb, 0);
1028 keyb_mode_edit ();
1029 }
1030 }
1031
1032 if (layouts.n_cust > 0)
1033 gtk_widget_set_sensitive (get_wg (cmb_variant), TRUE);
1034 else
1035 gtk_widget_set_sensitive (get_wg (cmb_variant), FALSE);
1036 }
1037 g_free (country_txt);
1038
1039 keyb_update_from_variant (cmb_country, cmb_variant);
1040
1041 callbacks_shield_set (FALSE);
1042 }
1043
1044 void
keyb_set_combo_kbd(gchar * cmb_country,gchar * cmb_variant)1045 keyb_set_combo_kbd (gchar *cmb_country, gchar *cmb_variant)
1046 {
1047 static gboolean init = FALSE;
1048 gchar *tmp;
1049 gint i, j;
1050 GtkComboBox *cmb;
1051
1052 callbacks_shield_set (TRUE);
1053
1054 if (! main_preferences_exist ("tutor", "keyboard"))
1055 main_preferences_set_string ("tutor", "keyboard", trans_get_default_keyboard ());
1056
1057 if (init == FALSE)
1058 {
1059 tmp = main_preferences_get_string ("tutor", "keyboard");
1060 if (tmp == NULL)
1061 g_error ("Unexpected keyboard layout, NULL");
1062 keyb_init_name (tmp);
1063 keyb_set_chars ();
1064 init = TRUE;
1065 g_free (tmp);
1066 }
1067
1068 keyb_set_keyboard_layouts (); // if already initialized, this sets only the custom layouts
1069
1070 cmb = GTK_COMBO_BOX (get_wg (cmb_country));
1071 gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (cmb), 0);
1072 keyb.cmb_n = 0;
1073 for (i = 0; i < layouts.n_orig; i++)
1074 {
1075 j = i - 1;
1076 while (j >= 0)
1077 {
1078 if (g_str_equal (layouts.orig[i].country, layouts.orig[j].country))
1079 break;
1080 j--;
1081 }
1082 if (j < 0)
1083 {
1084 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cmb), layouts.orig[i].country);
1085 keyb.cmb_n++;
1086 }
1087 }
1088 gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (cmb), KEYB_CUSTOM);
1089 keyb.cmb_n++;
1090
1091 keyb_update_combos (cmb_country, cmb_variant);
1092
1093 callbacks_shield_set (FALSE);
1094 }
1095
1096 void
keyb_update_combos(gchar * cmb_country,gchar * cmb_variant)1097 keyb_update_combos (gchar *cmb_country, gchar *cmb_variant)
1098 {
1099 gint i;
1100 GtkComboBox *cmb;
1101
1102 callbacks_shield_set (TRUE);
1103
1104 cmb = GTK_COMBO_BOX (get_wg (cmb_country));
1105
1106 for (i = 0; i < layouts.n_orig; i++)
1107 {
1108 if (g_str_equal (keyb.name, layouts.orig[i].name))
1109 break;
1110 }
1111 if (i < layouts.n_orig)
1112 {
1113 gchar *country;
1114 gchar *current;
1115
1116 /* Set original */
1117 for (i = 1; i < keyb.cmb_n; i++)
1118 {
1119
1120 gtk_combo_box_set_active (cmb, i);
1121 country = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cmb));
1122 current = keyb_get_country (keyb.name);
1123 if (g_str_equal (country, current))
1124 {
1125 g_free (country);
1126 g_free (current);
1127 break;
1128 }
1129 g_free (country);
1130 g_free (current);
1131 }
1132 if (i == keyb.cmb_n)
1133 gtk_combo_box_set_active (cmb, 0);
1134 }
1135 else /* Set custom */
1136 gtk_combo_box_set_active (cmb, 0);
1137
1138 keyb_set_combo_kbd_variant (cmb_country, cmb_variant);
1139
1140 callbacks_shield_set (FALSE);
1141 }
1142
1143 void
keyb_intro_step_next()1144 keyb_intro_step_next ()
1145 {
1146 if (keyb.intro_step < 7)
1147 keyb_intro_step (++keyb.intro_step);
1148 }
1149
1150 void
keyb_intro_step_previous()1151 keyb_intro_step_previous ()
1152 {
1153 if (keyb.intro_step > 0)
1154 keyb_intro_step (--keyb.intro_step);
1155 }
1156
1157 void
keyb_intro_step(gint step)1158 keyb_intro_step (gint step)
1159 {
1160 gchar *intro00;
1161 GtkLabel *tit;
1162 GtkLabel *tx1;
1163 GtkLabel *tx2;
1164 GtkTextBuffer *buffer;
1165 GtkWidget *wg;
1166
1167 static gchar *intro01 = NULL;
1168 static gchar *intro02 = NULL;
1169 static gchar *intro03 = NULL;
1170 static gchar *intro04 = NULL;
1171 static gchar *intro05 = NULL;
1172 static gchar *intro06 = NULL;
1173 static gchar *intro07 = NULL;
1174 static gchar *intro08 = NULL;
1175 static gchar *intro09 = NULL;
1176 static gchar *intro10 = NULL;
1177 static gchar *intro11 = NULL;
1178
1179 if (intro01 == NULL)
1180 {
1181 intro01 = g_strdup (_("Correct positioning of the hands and fingers is very important to efficient typing. "
1182 "You will learn faster and type better if you follow the next recommendations."));
1183 intro02 = g_strdup (_(
1184 "The index-finger tips rest over each of the two keys which have a small raised mark, "
1185 "in the center of the keyboard."));
1186 intro03 = g_strdup (_(
1187 "These marks function as 'tactile hooks' for your fingers to remain at the correct position. "
1188 "This way, with a little experience, you will not need to look at the keyboard to see if your "
1189 "fingers are properly positioned."));
1190 intro04 = g_strdup (_(
1191 "The tips of the other fingers lie naturally beside the index ones, "
1192 "over the keys on the same row of the keyboard."));
1193 intro05 = g_strdup (_(
1194 "The outside edges of your thumbs rest over the space bar."));
1195 intro06 = g_strdup (_(
1196 "The part of the hands closest to the wrist (the base) rest over the table, "
1197 "outside the keyboard. Without this kind of support the arms would quickly tire."));
1198 intro07 = g_strdup (_(
1199 "This is referred to as the home position for the hands. "
1200 "From it the fingers move all over the keyboard, "
1201 "reaching all the keys as naturally and quickly as possible. "
1202 "To reach this goal one uses a specific relation between each key and finger. "
1203 "This relation will be learned gradually as you complete the basic course."));
1204 intro08 = g_strdup (_(
1205 "When learning the relation between fingers and keys, "
1206 "it is very important that you only move the finger which must press the key "
1207 "and allow all other fingers to remain in the home position."));
1208 intro09 = g_strdup (_(
1209 "After memorizing this relationship, you can relax the previous rule some, "
1210 "so that you can attain greater speed while typing."));
1211 intro10 = g_strdup (_(
1212 "The shift keys are used for capital letters and for some symbols. "
1213 "To get a shifted key input you should first use the small finger of the opposite hand. "
1214 "Just keep it in the closest Shift while reaching the target key with the other hand."));
1215 intro11 = g_strdup (_(
1216 "You should be prepared to start training with the basic course. "
1217 "It will take effort and patience to be successful as a typist. "
1218 "We trust you have these and look forward to your success!"));
1219 }
1220
1221 tit = GTK_LABEL (get_wg ("label_keyboard_title"));
1222 tx1 = GTK_LABEL (get_wg ("label_keyboard_text_1"));
1223 tx2 = GTK_LABEL (get_wg ("label_keyboard_text_2"));
1224
1225 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (get_wg ("textview_keyboard")));
1226
1227 intro00 = g_strdup_printf (_("Step %i"), step);
1228 switch (step)
1229 {
1230 case 0: /* Recommendations */
1231 gtk_label_set_text (tit, _("To position the hands"));
1232 gtk_text_buffer_set_text (buffer, intro01, -1);
1233 gtk_widget_grab_focus (get_wg ("button_keyboard_next"));
1234 keyb_set_sensitive (TRUE);
1235 hints_demo_fingers (0);
1236 break;
1237 case 1: /* Index fingers */
1238 gtk_label_set_text (tit, intro00);
1239 gtk_text_buffer_set_text (buffer, intro02, -1);
1240 gtk_text_buffer_insert_at_cursor (buffer, "\n", -1);
1241 gtk_text_buffer_insert_at_cursor (buffer, intro03, -1);
1242 keyb_set_sensitive (FALSE);
1243 gtk_widget_set_sensitive (keyb.but[2][3], TRUE);
1244 gtk_widget_set_sensitive (keyb.but[2][6], TRUE);
1245 hints_demo_fingers (1000);
1246 break;
1247 case 2: /* Fingers beside index */
1248 gtk_label_set_text (tit, intro00);
1249 gtk_text_buffer_set_text (buffer, intro04, -1);
1250 keyb_set_sensitive (FALSE);
1251 gtk_widget_set_sensitive (keyb.but[2][0], TRUE);
1252 gtk_widget_set_sensitive (keyb.but[2][1], TRUE);
1253 gtk_widget_set_sensitive (keyb.but[2][2], TRUE);
1254 gtk_widget_set_sensitive (keyb.but[2][7], TRUE);
1255 gtk_widget_set_sensitive (keyb.but[2][8], TRUE);
1256 gtk_widget_set_sensitive (keyb.but[2][9], TRUE);
1257 hints_demo_fingers (1000/3);
1258 break;
1259 case 3: /* Thumbs and wrists */
1260 gtk_label_set_text (tit, intro00);
1261 gtk_text_buffer_set_text (buffer, intro05, -1);
1262 gtk_text_buffer_insert_at_cursor (buffer, "\n", -1);
1263 gtk_text_buffer_insert_at_cursor (buffer, intro06, -1);
1264 keyb_set_sensitive (FALSE);
1265 gtk_widget_set_sensitive (get_wg ("but_space"), TRUE);
1266 hints_demo_fingers (0);
1267 gtk_widget_grab_focus (get_wg ("but_space"));
1268 hints_update_from_button (GTK_BUTTON (get_wg ("but_space")));
1269 break;
1270 case 4: /* The home position */
1271 gtk_label_set_text (tit, intro00);
1272 gtk_text_buffer_set_text (buffer, intro07, -1);
1273 keyb_set_sensitive (FALSE);
1274 gtk_widget_set_sensitive (keyb.but[2][0], TRUE);
1275 gtk_widget_set_sensitive (keyb.but[2][1], TRUE);
1276 gtk_widget_set_sensitive (keyb.but[2][2], TRUE);
1277 gtk_widget_set_sensitive (keyb.but[2][3], TRUE);
1278 gtk_widget_set_sensitive (keyb.but[2][6], TRUE);
1279 gtk_widget_set_sensitive (keyb.but[2][7], TRUE);
1280 gtk_widget_set_sensitive (keyb.but[2][8], TRUE);
1281 gtk_widget_set_sensitive (keyb.but[2][9], TRUE);
1282 hints_demo_fingers (1000/4);
1283 break;
1284 case 5: /* Reaching keys */
1285 gtk_label_set_text (tit, intro00);
1286 gtk_text_buffer_set_text (buffer, intro08, -1);
1287 gtk_text_buffer_insert_at_cursor (buffer, "\n", -1);
1288 gtk_text_buffer_insert_at_cursor (buffer, intro09, -1);
1289 keyb_set_sensitive (TRUE);
1290 gtk_widget_set_sensitive (keyb.but[2][0], FALSE);
1291 gtk_widget_set_sensitive (keyb.but[2][1], FALSE);
1292 gtk_widget_set_sensitive (keyb.but[2][2], FALSE);
1293 gtk_widget_set_sensitive (keyb.but[2][3], FALSE);
1294 gtk_widget_set_sensitive (keyb.but[2][6], FALSE);
1295 gtk_widget_set_sensitive (keyb.but[2][7], FALSE);
1296 gtk_widget_set_sensitive (keyb.but[2][8], FALSE);
1297 gtk_widget_set_sensitive (keyb.but[2][9], FALSE);
1298 gtk_widget_set_sensitive (get_wg ("but_space"), FALSE);
1299 gtk_widget_set_sensitive (get_wg ("toggle_shift1"), FALSE);
1300 gtk_widget_set_sensitive (get_wg ("toggle_shift2"), FALSE);
1301 hints_demo_fingers (1000/5);
1302 break;
1303 case 6: /* Shift key */
1304 gtk_label_set_text (tit, intro00);
1305 gtk_text_buffer_set_text (buffer, intro10, -1);
1306 keyb_set_sensitive (FALSE);
1307 gtk_widget_set_sensitive (keyb.but[0][0], TRUE);
1308 gtk_widget_set_sensitive (keyb.but[0][13], TRUE);
1309 gtk_widget_set_sensitive (get_wg ("toggle_shift1"), TRUE);
1310 gtk_widget_set_sensitive (get_wg ("toggle_shift2"), TRUE);
1311 hints_demo_fingers (1000/2);
1312 break;
1313 case 7: /* Final words */
1314 gtk_label_set_text (tit, _("Go ahead!"));
1315 gtk_text_buffer_set_text (buffer, intro11, -1);
1316 gtk_widget_grab_focus (get_wg ("button_keyboard_close"));
1317 keyb_set_sensitive (TRUE);
1318 hints_demo_fingers (0);
1319 break;
1320 default:
1321 g_free (intro00);
1322 intro00 = g_strdup (_("Click on any key to see which finger you must use:"));
1323 gtk_label_set_text (tit, _("Relation between fingers and keys"));
1324 gtk_label_set_text (tx1, _("Click on any key to see which finger you must use:"));
1325 gtk_label_set_text (tx2, "");
1326 gtk_text_buffer_set_text (buffer, intro00, -1);
1327 keyb_set_sensitive (TRUE);
1328 hints_demo_fingers (0);
1329 }
1330 g_free (intro00);
1331
1332 /* Blind people want no fancy autonomous buttons jumping around */
1333 wg = get_wg ("checkbutton_speech");
1334 if (gtk_widget_get_visible (wg) && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wg)))
1335 hints_demo_fingers (0);
1336
1337 if (step == 0)
1338 gtk_widget_set_sensitive (get_wg ("button_keyboard_previous"), FALSE);
1339 else
1340 gtk_widget_set_sensitive (get_wg ("button_keyboard_previous"), TRUE);
1341
1342 if (step == 7)
1343 gtk_widget_set_sensitive (get_wg ("button_keyboard_next"), FALSE);
1344 else
1345 gtk_widget_set_sensitive (get_wg ("button_keyboard_next"), TRUE);
1346
1347 if (step >= 0 && step <= 7)
1348 keyb.intro_step = step;
1349 else
1350 keyb.intro_step = 0;
1351 }
1352
1353 gchar *
keyb_mode_get_name()1354 keyb_mode_get_name ()
1355 {
1356 gchar *country;
1357 gchar *variant;
1358 static gchar *kbname = NULL;
1359
1360 if (kbname != NULL)
1361 g_free (kbname);
1362
1363 country = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (get_wg ("combobox_kbd_country")));
1364 variant = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (get_wg ("combobox_kbd_variant")));
1365 kbname = g_strdup_printf ("%s - %s", country, variant);
1366 g_free (country);
1367 g_free (variant);
1368
1369 return (kbname);
1370 }
1371
1372 void
keyb_mode_intro()1373 keyb_mode_intro ()
1374 {
1375 gchar *tit;
1376
1377 keyb_update_virtual_layout ();
1378 gtk_widget_hide (get_wg ("window_hints"));
1379 keyb_edit_none ();
1380
1381 tit = g_strdup_printf ("%s - %s: %s", _("Introduction"), _("Keyboard"), keyb_mode_get_name ());
1382 gtk_window_set_title (get_win ("window_keyboard"), tit);
1383 g_free (tit);
1384
1385 gtk_window_set_resizable (get_win ("window_keyboard"), TRUE);
1386 gtk_widget_set_size_request (get_wg ("window_keyboard"), -1, 420);
1387 gtk_widget_hide (get_wg ("label_keyboard_text_1"));
1388 gtk_widget_hide (get_wg ("button_kb_save"));
1389 gtk_widget_hide (get_wg ("button_keyboard_hands"));
1390 gtk_widget_hide (get_wg ("button_keyboard_cancel"));
1391 gtk_widget_hide (get_wg ("hbox_keyboard_selector"));
1392 gtk_widget_hide (get_wg ("hbox_keyboard_saveas"));
1393 gtk_widget_show (get_wg ("label_keyboard_spacer"));
1394 gtk_widget_show (get_wg ("scrolledwindow_keyboard"));
1395 gtk_widget_show (get_wg ("button_keyboard_close"));
1396 gtk_widget_show (get_wg ("button_keyboard_previous"));
1397 gtk_widget_show (get_wg ("button_keyboard_next"));
1398 gtk_widget_show (get_wg ("hbox_keyboard_hints"));
1399
1400 hints_set_tips ();
1401 keyb_intro_step (0);
1402
1403 gtk_widget_show (get_wg ("window_keyboard"));
1404 }
1405
1406 void
keyb_mode_hint()1407 keyb_mode_hint ()
1408 {
1409 gchar *tit;
1410
1411 keyb_update_virtual_layout ();
1412 gtk_widget_hide (get_wg ("window_hints"));
1413 keyb_edit_none ();
1414
1415 tit = g_strdup_printf ("%s: %s", _("Keyboard"), keyb_mode_get_name ());
1416 gtk_window_set_title (get_win ("window_keyboard"), tit);
1417 g_free (tit);
1418
1419 gtk_window_set_resizable (get_win ("window_keyboard"), FALSE);
1420 gtk_widget_set_size_request (get_wg ("window_keyboard"), -1, -1);
1421 gtk_widget_hide (get_wg ("label_keyboard_spacer"));
1422 gtk_widget_hide (get_wg ("scrolledwindow_keyboard"));
1423 gtk_widget_hide (get_wg ("button_kb_save"));
1424 gtk_widget_hide (get_wg ("button_keyboard_cancel"));
1425 gtk_widget_hide (get_wg ("button_keyboard_previous"));
1426 gtk_widget_hide (get_wg ("button_keyboard_next"));
1427 gtk_widget_hide (get_wg ("hbox_keyboard_selector"));
1428 gtk_widget_hide (get_wg ("hbox_keyboard_saveas"));
1429 gtk_widget_show (get_wg ("label_keyboard_text_1"));
1430 gtk_widget_show (get_wg ("button_keyboard_hands"));
1431 gtk_widget_show (get_wg ("button_keyboard_close"));
1432 gtk_widget_show (get_wg ("hbox_keyboard_hints"));
1433
1434 hints_set_tips ();
1435 keyb_intro_step (-1);
1436 gtk_widget_grab_focus (get_wg ("button_keyboard_close"));
1437
1438 gtk_widget_show (get_wg ("window_keyboard"));
1439 }
1440
1441 void
keyb_mode_edit()1442 keyb_mode_edit ()
1443 {
1444 gchar *tmp;
1445
1446 /* Save the current name as 'name_last' */
1447 tmp = g_strdup (keyb.name);
1448 keyb_set_name (tmp);
1449 g_free (tmp);
1450
1451 keyb_set_modified_status (FALSE);
1452 if (layouts.n_cust == 0)
1453 gtk_widget_set_sensitive (get_wg ("button_kb_remove"), FALSE);
1454 else
1455 gtk_widget_set_sensitive (get_wg ("button_kb_remove"), TRUE);
1456
1457 keyb_update_combos ("combobox_keyboard_country", "combobox_keyboard_variant");
1458
1459 keyb_update_virtual_layout ();
1460 keyb_edit_none ();
1461
1462 gtk_window_set_title (get_win ("window_keyboard"), _("Create or modify a custom keyboard layout"));
1463 gtk_window_set_resizable (get_win ("window_keyboard"), FALSE);
1464 gtk_widget_set_size_request (get_wg ("window_keyboard"), -1, -1);
1465 gtk_widget_hide (get_wg ("button_keyboard_hands"));
1466 gtk_widget_hide (get_wg ("button_keyboard_close"));
1467 gtk_widget_hide (get_wg ("button_keyboard_previous"));
1468 gtk_widget_hide (get_wg ("button_keyboard_next"));
1469 gtk_widget_hide (get_wg ("hbox_keyboard_hints"));
1470 gtk_widget_show (get_wg ("button_keyboard_cancel"));
1471 gtk_widget_show (get_wg ("hbox_keyboard_selector"));
1472 gtk_widget_show (get_wg ("hbox_keyboard_saveas"));
1473 gtk_widget_show (get_wg ("button_kb_save"));
1474
1475 keyb_set_sensitive (TRUE);
1476 gtk_widget_set_sensitive (get_wg ("but_space"), FALSE);
1477
1478 hints_set_tips ();
1479 gtk_widget_grab_focus (get_wg ("button_keyboard_cancel"));
1480
1481 gtk_widget_show (get_wg ("window_keyboard"));
1482 }
1483
1484 void
keyb_set_sensitive(gboolean state)1485 keyb_set_sensitive (gboolean state)
1486 {
1487 gint i, j;
1488 gint j_max;
1489
1490 for (i = 0; i < 4; i++)
1491 {
1492 j_max = KEY_LINE_LEN - (i == 0 ? 1 : (i == 1 ? 2 : 3));
1493 for (j = 0; j < j_max; j++)
1494 gtk_widget_set_sensitive (keyb.but[i][j], state);
1495 }
1496 gtk_widget_set_sensitive (get_wg ("but_space"), state);
1497 gtk_widget_set_sensitive (get_wg ("toggle_shift1"), state);
1498 gtk_widget_set_sensitive (get_wg ("toggle_shift2"), state);
1499 }
1500
1501 gboolean
keyb_button_match(GtkButton * button)1502 keyb_button_match (GtkButton * button)
1503 {
1504 gint i, j;
1505 gint j_max;
1506
1507 for (i = 0; i < 4; i++)
1508 {
1509 j_max = KEY_LINE_LEN - (i == 0 ? 1 : (i == 1 ? 2 : 3));
1510 for (j = 0; j < j_max; j++)
1511 {
1512 if (keyb.but[i][j] == GTK_WIDGET (button))
1513 {
1514 keyb.pos.i = i;
1515 keyb.pos.j = j;
1516 return TRUE;
1517 }
1518 }
1519 }
1520 return FALSE;
1521 }
1522
1523 /**********************************************************************
1524 * Moves the entry widget to get a real key pressed and
1525 * writes the name of the virtual one.
1526 */
1527 void
keyb_edit_button(GtkButton * button)1528 keyb_edit_button (GtkButton * button)
1529 {
1530 if (! keyb_button_match (button))
1531 return;
1532
1533 callbacks_shield_set (TRUE);
1534
1535 gtk_widget_grab_focus (GTK_WIDGET (button));
1536 gtk_entry_set_text (GTK_ENTRY (keyb.entry),
1537 gtk_label_get_text (GTK_LABEL (keyb.lab[keyb.pos.i][keyb.pos.j])));
1538 gtk_fixed_move (GTK_FIXED (get_wg ("fixed_keyboard")), keyb.entry,
1539 2 + x0[keyb.pos.i] + KEY_DX * keyb.pos.j,
1540 2 + KEY_DY * keyb.pos.i);
1541 if (keyb.pos.i == 1 && keyb.pos.j == 12)
1542 gtk_widget_set_size_request (keyb.entry, 49, 28);
1543 else
1544 gtk_widget_set_size_request (keyb.entry, 28, 28);
1545 gtk_editable_select_region (GTK_EDITABLE (keyb.entry), 0, 1);
1546 gtk_widget_show (keyb.entry);
1547 gtk_widget_grab_focus (keyb.entry);
1548
1549 callbacks_shield_set (FALSE);
1550 }
1551
1552 void
keyb_edit_none(void)1553 keyb_edit_none (void)
1554 {
1555 gtk_widget_hide (keyb.entry);
1556 }
1557
1558 gboolean
keyb_edit_next(void)1559 keyb_edit_next (void)
1560 {
1561 keyb.pos.j++;
1562
1563 switch (keyb.pos.i)
1564 {
1565 case 0:
1566 if (keyb.pos.j > 13)
1567 {
1568 keyb.pos.i++;
1569 keyb.pos.j = 0;
1570 }
1571 break;
1572 case 1:
1573 if (keyb.pos.j > 12)
1574 {
1575 keyb.pos.i++;
1576 keyb.pos.j = 0;
1577 }
1578 break;
1579 default:
1580 if (keyb.pos.j > 11)
1581 {
1582 keyb.pos.i++;
1583 keyb.pos.j = 0;
1584 }
1585 }
1586
1587 if (keyb.pos.i > 3)
1588 keyb.pos.i = 0;
1589
1590 gtk_widget_hide (keyb.entry);
1591 if (gtk_widget_get_sensitive (keyb.but[keyb.pos.i][keyb.pos.j]))
1592 {
1593 gtk_widget_grab_focus (keyb.but[keyb.pos.i][keyb.pos.j]);
1594 return TRUE;
1595 }
1596 else
1597 return FALSE;
1598 }
1599
1600 /**********************************************************************
1601 * Apply the key pressed to the virtual keyboard
1602 * and to the upper or lower character sets
1603 */
1604 void
keyb_change_key(gunichar real_key)1605 keyb_change_key (gunichar real_key)
1606 {
1607 gint key_lin, key_col;
1608 gunichar str_char;
1609 gchar tmp_utf8[7];
1610 gboolean tog_state;
1611 GtkWidget *wg;
1612
1613 key_lin = keyb.pos.i;
1614 key_col = keyb.pos.j;
1615
1616 str_char = g_unichar_toupper (real_key);
1617 tmp_utf8[g_unichar_to_utf8 (str_char, tmp_utf8)] = '\0';
1618 gtk_label_set_text (GTK_LABEL (keyb.lab[key_lin][key_col]), tmp_utf8);
1619
1620 wg = get_wg ("toggle_shift1");
1621 tog_state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wg));
1622 if (tog_state)
1623 {
1624 keyb.upchars[key_lin][key_col] = str_char;
1625 if (str_char >= L'A' && str_char <= L'Z')
1626 keyb.lochars[key_lin][key_col] = g_unichar_tolower (str_char);
1627 }
1628 else
1629 {
1630 keyb.lochars[key_lin][key_col] = g_unichar_tolower (str_char);
1631 if (str_char >= L'A' && str_char <= L'Z')
1632 keyb.upchars[key_lin][key_col] = str_char;
1633 }
1634
1635 keyb_set_modified_status (TRUE);
1636 gtk_widget_set_sensitive (get_wg ("button_kb_save"), TRUE);
1637 gtk_widget_set_sensitive (get_wg ("combobox_keyboard_country"), FALSE);
1638 gtk_widget_set_sensitive (get_wg ("combobox_keyboard_variant"), FALSE);
1639 }
1640
1641 /*******************************************************************************
1642 * Get an utf8 string for the par symbol
1643 */
1644 gchar *
keyb_get_utf8_paragraph_symbol()1645 keyb_get_utf8_paragraph_symbol ()
1646 {
1647 static gchar parsym[7] = {0, 0, 0, 0, 0, 0, 0};
1648 static gboolean is_initialized = FALSE;
1649
1650 if (is_initialized == FALSE)
1651 {
1652 is_initialized = TRUE;
1653 g_unichar_to_utf8 (UPSYM, parsym);
1654 }
1655 return (parsym);
1656 }
1657
1658 /*******************************************************************************
1659 * Initialize the hints mapping array
1660 */
1661 static gchar hints[4][KEY_LINE_LEN + 1];
1662 static gboolean hints_is_initialized = FALSE;
1663
1664 void
hints_init()1665 hints_init ()
1666 {
1667 gint i;
1668 gchar *tmp_name;
1669 gchar *tmp;
1670 FILE *fh;
1671
1672 if (hints_is_initialized == TRUE)
1673 return;
1674
1675 tmp_name = g_build_filename ("/etc/klavaro/fingers_position.txt", NULL);
1676 if (!g_file_test (tmp_name, G_FILE_TEST_IS_REGULAR))
1677 {
1678 g_free (tmp_name);
1679 tmp_name = g_build_filename (main_path_user (), "fingers_position.txt", NULL);
1680 }
1681 if (!g_file_test (tmp_name, G_FILE_TEST_IS_REGULAR))
1682 {
1683 g_free (tmp_name);
1684 tmp_name = g_build_filename (main_path_data (), "fingers_position.txt", NULL);
1685 }
1686 fh = (FILE *) g_fopen (tmp_name, "r");
1687 if (fh)
1688 {
1689 hints_is_initialized = TRUE;
1690 for (i = 0; i < 4; i++)
1691 tmp = fgets (hints[i], KEY_LINE_LEN + 1, fh);
1692 fclose (fh);
1693 hints_set_tips ();
1694 hints_set_colors ();
1695 }
1696 else
1697 g_warning ("couldn't open the file:\n %s", tmp_name);
1698 g_free (tmp_name);
1699
1700 }
1701
1702 gchar *
hints_string_from_charcode(gchar charcode)1703 hints_string_from_charcode (gchar charcode)
1704 {
1705 gchar *fingerhint = NULL;
1706
1707 switch (charcode)
1708 {
1709 case '1':
1710 fingerhint = g_strdup (_("small finger"));
1711 break;
1712 case '2':
1713 fingerhint = g_strdup (_("ring finger"));
1714 break;
1715 case '3':
1716 fingerhint = g_strdup (_("middle finger"));
1717 break;
1718 case '4':
1719 fingerhint = g_strdup (_("index finger"));
1720 break;
1721 case '5':
1722 fingerhint = g_strdup (_("thumbs"));
1723 break;
1724 case '6':
1725 fingerhint = g_strdup (_("index finger"));
1726 break;
1727 case '7':
1728 fingerhint = g_strdup (_("middle finger"));
1729 break;
1730 case '8':
1731 fingerhint = g_strdup (_("ring finger"));
1732 break;
1733 case '9':
1734 fingerhint = g_strdup (_("small finger"));
1735 break;
1736 default:
1737 fingerhint = g_strdup ("???");
1738 }
1739 return (fingerhint);
1740 }
1741
1742 gchar *
hints_color_from_charcode(gchar charcode)1743 hints_color_from_charcode (gchar charcode)
1744 {
1745 static gchar *hlp = NULL;
1746
1747 g_free (hlp);
1748
1749 switch (charcode)
1750 {
1751 case '1':
1752 if (main_preferences_exist ("colors", "key_1"))
1753 hlp = main_preferences_get_string ("colors", "key_1");
1754 else
1755 hlp = g_strdup (KEYB_BLUE);
1756 break;
1757 case '2':
1758 if (main_preferences_exist ("colors", "key_2"))
1759 hlp = main_preferences_get_string ("colors", "key_2");
1760 else
1761 hlp = g_strdup (KEYB_RED);
1762 break;
1763 case '3':
1764 if (main_preferences_exist ("colors", "key_3"))
1765 hlp = main_preferences_get_string ("colors", "key_3");
1766 else
1767 hlp = g_strdup (KEYB_GREEN);
1768 break;
1769 case '4':
1770 if (main_preferences_exist ("colors", "key_4"))
1771 hlp = main_preferences_get_string ("colors", "key_4");
1772 else
1773 hlp = g_strdup (KEYB_YELLOW);
1774 break;
1775 case '5':
1776 if (main_preferences_exist ("colors", "key_5"))
1777 hlp = main_preferences_get_string ("colors", "key_5");
1778 else
1779 hlp = g_strdup (KEYB_PURPLE);
1780 break;
1781 case '6':
1782 if (main_preferences_exist ("colors", "key_6"))
1783 hlp = main_preferences_get_string ("colors", "key_6");
1784 else
1785 hlp = g_strdup (KEYB_ORANGE);
1786 break;
1787 case '7':
1788 if (main_preferences_exist ("colors", "key_7"))
1789 hlp = main_preferences_get_string ("colors", "key_7");
1790 else
1791 hlp = g_strdup (KEYB_GREEN);
1792 break;
1793 case '8':
1794 if (main_preferences_exist ("colors", "key_8"))
1795 hlp = main_preferences_get_string ("colors", "key_8");
1796 else
1797 hlp = g_strdup (KEYB_RED);
1798 break;
1799 case '9':
1800 if (main_preferences_exist ("colors", "key_9"))
1801 hlp = main_preferences_get_string ("colors", "key_9");
1802 else
1803 hlp = g_strdup (KEYB_BLUE);
1804 break;
1805 default:
1806 hlp = g_strdup ("#AFAFAF");
1807 }
1808 return hlp;
1809 }
1810
1811 void
hints_set_tips()1812 hints_set_tips ()
1813 {
1814 static gchar *editme = NULL;
1815 gint i, j;
1816 gint j_max;
1817 gchar *tmp;
1818
1819 if (editme == NULL)
1820 editme = g_strdup (_("Press and edit me"));
1821
1822 if (hints_is_initialized == FALSE)
1823 {
1824 g_warning ("Not able to set keyboard tips without initializing the hints");
1825 return;
1826 }
1827
1828 for (i = 0; i < 4; i++)
1829 {
1830 j_max = KEY_LINE_LEN - (i == 0 ? 1 : (i == 1 ? 2 : 3));
1831 for (j = 0; j < j_max; j++)
1832 {
1833 tmp = hints_string_from_charcode (hints[i][j]);
1834 if (! gtk_widget_get_visible (get_wg ("hbox_keyboard_hints")))
1835 gtk_widget_set_tooltip_text (keyb.but[i][j], editme);
1836 else
1837 gtk_widget_set_tooltip_text (keyb.but[i][j], tmp);
1838 g_free (tmp);
1839 }
1840 }
1841 }
1842
1843 void
hints_set_colors()1844 hints_set_colors ()
1845 {
1846 gint i, j;
1847 gint j_max;
1848 GdkRGBA color;
1849 GtkStyleContext *sc;
1850 gchar *tmp;
1851
1852 if (hints_is_initialized == FALSE)
1853 {
1854 g_warning ("Not able to set keyboard colors without initializing the hints");
1855 return;
1856 }
1857
1858 for (i = 0; i < 4; i++)
1859 {
1860 j_max = KEY_LINE_LEN - (i == 0 ? 1 : (i == 1 ? 2 : 3));
1861 for (j = 0; j < j_max; j++)
1862 {
1863 sc = gtk_widget_get_style_context (keyb.but[i][j]);
1864 gtk_style_context_add_provider (sc, GTK_STYLE_PROVIDER (keyb_css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1865 tmp = g_strdup_printf ("key-but%c", hints[i][j]);
1866 gtk_style_context_add_class (sc, tmp);
1867 g_free (tmp);
1868 }
1869 }
1870 }
1871
1872 /* Update the image of the window_keyboard
1873 * Maps the button to the file which shows the finger associated with its key
1874 */
1875 void
hints_update_from_button(GtkButton * button)1876 hints_update_from_button (GtkButton *button)
1877 {
1878 gchar *pix_name;
1879 gchar ch;
1880
1881 hints_init (); // if already initialized, do nothing
1882
1883 if (keyb_button_match (button))
1884 {
1885 pix_name = g_strdup ("hands_0.png");
1886 ch = hints[keyb.pos.i][keyb.pos.j];
1887 if (ch >= '1' && ch <= '9')
1888 pix_name[6] = ch;
1889 }
1890 else if ( button == GTK_BUTTON (get_wg ("but_space")) )
1891 pix_name = g_strdup ("hands_5.png");
1892 else if ( button == GTK_BUTTON (get_wg ("toggle_shift1")) )
1893 pix_name = g_strdup ("hands_1.png");
1894 else if ( button == GTK_BUTTON (get_wg ("toggle_shift2")) )
1895 pix_name = g_strdup ("hands_9.png");
1896 else
1897 pix_name = g_strdup ("hands_0.png");
1898
1899 set_pixmap ("pixmap_hints_fixed", pix_name);
1900 g_free (pix_name);
1901 }
1902
1903 /* Update the image of the window_hints
1904 * Maps the character to the file which shows the finger associated with that key
1905 */
1906 void
hints_update_from_char(gunichar character)1907 hints_update_from_char (gunichar character)
1908 {
1909 gchar file_name[32];
1910 gint i, j;
1911
1912 if (! gtk_widget_get_visible (get_wg ("window_hints")))
1913 return;
1914
1915 strcpy (file_name, "hands_0.png");
1916 if (character == UPSYM)
1917 strcpy (file_name, "hands_9.png");
1918 else if (character == L' ')
1919 strcpy (file_name, "hands_5.png");
1920 else if (character != 0)
1921 {
1922 hints_init (); // if already initialized, do nothing
1923
1924 for (i = 3; i >= 0; i--)
1925 for (j = 0; j < 15; j++)
1926 if (character == keyb.lochars[i][j])
1927 {
1928 file_name[6] = hints[i][j];
1929 set_pixmap ("pixmap_hints", file_name);
1930 return;
1931 }
1932
1933 for (i = 3; i >= 0; i--)
1934 for (j = 0; j < 15; j++)
1935 if (character == keyb.upchars[i][j])
1936 {
1937 file_name[6] = hints[i][j];
1938 set_pixmap ("pixmap_hints", file_name);
1939 return;
1940 }
1941 file_name[6] = '0';
1942 }
1943
1944 set_pixmap ("pixmap_hints", file_name);
1945 }
1946
1947 gboolean
hints_demo_fingers_move(gpointer data)1948 hints_demo_fingers_move (gpointer data)
1949 {
1950 static int i = 0;
1951
1952 if (data)
1953 {
1954 keyb.pos.i = 0;
1955 keyb.pos.j = 0;
1956 }
1957
1958 for (i = 0; i < 500; i++)
1959 if (keyb_edit_next ())
1960 break;
1961 return TRUE;
1962 }
1963
1964 void
hints_demo_fingers(guint msec)1965 hints_demo_fingers (guint msec)
1966 {
1967 static GSource *source = NULL;
1968 guint id;
1969
1970 if (source != NULL)
1971 g_source_destroy (source);
1972 source = NULL;
1973 hints_demo_fingers_move (&msec);
1974
1975 if (msec != 0)
1976 {
1977 id = g_timeout_add (msec, hints_demo_fingers_move, NULL);
1978 source = g_main_context_find_source_by_id (NULL, id);
1979 }
1980 }
1981
1982 gchar *
hints_finger_name_from_char(gunichar uch)1983 hints_finger_name_from_char (gunichar uch)
1984 {
1985 gint i, j;
1986
1987 if (uch == UPSYM || uch == L'\n' || uch == L'\r')
1988 return (hints_string_from_charcode ('9'));
1989 if (uch == L' ')
1990 return (hints_string_from_charcode ('5'));
1991
1992 hints_init (); // if already initialized, do nothing
1993
1994 for (i = 3; i >= 0; i--)
1995 for (j = 0; j < 15; j++)
1996 if (uch == keyb.lochars[i][j])
1997 return (hints_string_from_charcode (hints[i][j]));
1998
1999 for (i = 3; i >= 0; i--)
2000 for (j = 0; j < 15; j++)
2001 if (uch == keyb.upchars[i][j])
2002 return (hints_string_from_charcode (hints[i][j]));
2003
2004 return (g_strdup (" "));
2005 }
2006
2007