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 #include <string.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <glib.h>
18 #include <glib/gstdio.h>
19 #include <pango/pango-attributes.h>
20 #include <gtk/gtk.h>
21 #include <curl/curl.h>
22
23 #include "auxiliar.h"
24 #include "callbacks.h"
25 #include "translation.h"
26 #include "keyboard.h"
27 #include "tutor.h"
28 #include "accuracy.h"
29 #include "top10.h"
30 #include "main.h"
31
32 /*******************************************************************************
33 * Variables
34 */
35 GtkBuilder *gui;
36
37 gchar *KEYB_CUSTOM;
38 gchar *KEYB_EDIT;
39 gchar *OTHER_DEFAULT;
40
41 static GKeyFile *preferences = NULL;
42 static GKeyFile *altcolor = NULL;
43 static gboolean curl_ok;
44 static gboolean velo_txt;
45 static struct
46 {
47 gchar *user;
48 gchar *stats;
49 gchar *data;
50 gchar *score;
51 } path;
52
53 /*******************************************************************************
54 * Interface functions
55 */
56 gchar *
main_path_user()57 main_path_user ()
58 {
59 return (path.user);
60 }
61
62 gchar *
main_path_stats()63 main_path_stats ()
64 {
65 return (path.stats);
66 }
67
68 gchar *
main_path_data()69 main_path_data ()
70 {
71 return (path.data);
72 }
73
74 gchar *
main_path_score()75 main_path_score ()
76 {
77 return (path.score);
78 }
79
80 gboolean
main_curl_ok()81 main_curl_ok ()
82 {
83 return (curl_ok);
84 }
85
86 gboolean
main_velo_txt()87 main_velo_txt ()
88 {
89 return (velo_txt);
90 }
91
92 gboolean
main_preferences_exist(gchar * group,gchar * key)93 main_preferences_exist (gchar * group, gchar * key)
94 {
95 return (g_key_file_has_key (preferences, group, key, NULL));
96 }
97
98 void
main_preferences_remove(gchar * group,gchar * key)99 main_preferences_remove (gchar * group, gchar * key)
100 {
101 g_key_file_remove_key (preferences, group, key, NULL);
102 }
103
104 gchar *
main_preferences_get_string(gchar * group,gchar * key)105 main_preferences_get_string (gchar * group, gchar * key)
106 {
107 return (g_key_file_get_string (preferences, group, key, NULL));
108 }
109
110 void
main_preferences_set_string(gchar * group,gchar * key,gchar * value)111 main_preferences_set_string (gchar * group, gchar * key, gchar * value)
112 {
113 g_key_file_set_string (preferences, group, key, value);
114 }
115
116 gint
main_preferences_get_int(gchar * group,gchar * key)117 main_preferences_get_int (gchar * group, gchar * key)
118 {
119 return (g_key_file_get_integer (preferences, group, key, NULL));
120 }
121
122 void
main_preferences_set_int(gchar * group,gchar * key,gint value)123 main_preferences_set_int (gchar * group, gchar * key, gint value)
124 {
125 g_key_file_set_integer (preferences, group, key, value);
126 }
127
128 gboolean
main_preferences_get_boolean(gchar * group,gchar * key)129 main_preferences_get_boolean (gchar * group, gchar * key)
130 {
131 return (g_key_file_get_boolean (preferences, group, key, NULL));
132 }
133
134 void
main_preferences_set_boolean(gchar * group,gchar * key,gboolean value)135 main_preferences_set_boolean (gchar * group, gchar * key, gboolean value)
136 {
137 g_key_file_set_boolean (preferences, group, key, value);
138 }
139
140 gboolean
main_altcolor_exist(gchar * group,gchar * key)141 main_altcolor_exist (gchar * group, gchar * key)
142 {
143 return (g_key_file_has_key (altcolor, group, key, NULL));
144 }
145
146 gchar *
main_altcolor_get_string(gchar * group,gchar * key)147 main_altcolor_get_string (gchar * group, gchar * key)
148 {
149 return (g_key_file_get_string (altcolor, group, key, NULL));
150 }
151
152
153 gboolean
main_altcolor_get_boolean(gchar * group,gchar * key)154 main_altcolor_get_boolean (gchar * group, gchar * key)
155 {
156 return (g_key_file_get_boolean (altcolor, group, key, NULL));
157 }
158
159 void
main_altcolor_set_boolean(gchar * group,gchar * key,gboolean value)160 main_altcolor_set_boolean (gchar * group, gchar * key, gboolean value)
161 {
162 g_key_file_set_boolean (altcolor, group, key, value);
163 }
164
165 void
main_preferences_save()166 main_preferences_save ()
167 {
168 gchar *tmp_name;
169 FILE *fh;
170
171 /* Save preferences
172 */
173 assert_user_dir ();
174 tmp_name = g_build_filename (main_path_user (), "preferences.ini", NULL);
175 fh = (FILE *) g_fopen (tmp_name, "w");
176 if (fh == NULL)
177 g_warning ("couldn't save your preferences at the user folder:\n %s", tmp_name);
178 else
179 {
180 g_free (tmp_name);
181 tmp_name = NULL;
182 tmp_name = g_key_file_to_data (preferences, NULL, NULL);
183 if (tmp_name != NULL)
184 fputs (tmp_name, fh);
185 else
186 g_warning ("no preferences to be saved!");
187 fclose (fh);
188 }
189 g_free (tmp_name);
190
191 /* Save altcolor
192 */
193 tmp_name = g_build_filename (main_path_user (), "altcolor.ini", NULL);
194 fh = (FILE *) g_fopen (tmp_name, "w");
195 if (fh == NULL)
196 g_warning ("couldn't save your altcolor file at the user folder:\n %s", tmp_name);
197 else
198 {
199 g_free (tmp_name);
200 tmp_name = NULL;
201 tmp_name = g_key_file_to_data (altcolor, NULL, NULL);
202 if (tmp_name != NULL)
203 fputs (tmp_name, fh);
204 else
205 g_warning ("no altcolors to be saved!");
206 fclose (fh);
207 }
208 g_free (tmp_name);
209 }
210
211 /*
212 * End of interface, start of private functions
213 */
214
215 /**********************************************************************
216 * Initialize the value of the global variables
217 */
218 static void
main_initialize_global_variables()219 main_initialize_global_variables ()
220 {
221 gchar *tmp;
222 FILE *fh;
223
224 /* Set the home user dir
225 */
226 path.user = g_build_filename (g_get_user_config_dir (), "klavaro", NULL);
227 if (!g_file_test (path.user, G_FILE_TEST_IS_DIR))
228 {
229 if (g_mkdir_with_parents (path.user, DIR_PERM) == -1)
230 g_error ("Sorry, not able to create the user config dir: %s", path.user);
231 }
232
233 /* Set the home user stats dir (~/.local/share dir)
234 */
235 if (UNIX_OK)
236 path.stats = g_build_filename (g_get_user_data_dir (), "klavaro", NULL);
237 else
238 path.stats = g_build_filename (g_get_user_config_dir (), "klavaro", NULL);
239 if (!g_file_test (path.stats, G_FILE_TEST_IS_DIR))
240 {
241 g_mkdir_with_parents (path.stats, DIR_PERM);
242 }
243
244 /* Get a valid data path.
245 * First searches the path at the source directory.
246 */
247 path.data = g_build_filename ("..", "data", NULL);
248 tmp = g_build_filename (path.data, "basic_lessons.txt", NULL);
249 if (!g_file_test (tmp, G_FILE_TEST_EXISTS))
250 {
251 g_free (path.data);
252 g_free (tmp);
253 path.data = g_build_filename (PACKAGE_DATA_DIR, PACKAGE, NULL);
254 tmp = g_build_filename (path.data, "basic_lessons.txt", NULL);
255 }
256 if (! g_file_test (tmp, G_FILE_TEST_EXISTS))
257 g_error ("couldn't find a data file at the data path:\n %s", tmp);
258 g_free (tmp);
259
260 /* Get a valid scoring path.
261 */
262 path.score = g_build_filename (path.stats, "ksc", NULL);
263 if (!g_file_test (path.score, G_FILE_TEST_IS_DIR))
264 {
265 g_mkdir_with_parents (path.score, DIR_PERM);
266 }
267
268 /* Retrieve saved preferences or initial one
269 */
270 preferences = g_key_file_new ();
271 tmp = g_build_filename (main_path_user (), "preferences.ini", NULL);
272 if (!g_file_test (tmp, G_FILE_TEST_EXISTS))
273 {
274 g_free (tmp);
275 tmp = g_strdup ("/etc/klavaro/preferences.ini");
276 }
277 g_key_file_load_from_file (preferences, tmp, G_KEY_FILE_NONE, NULL);
278 g_free (tmp);
279
280 /* Retrieve saved alternative colors or initial ones
281 */
282 altcolor = g_key_file_new ();
283 tmp = g_build_filename (main_path_user (), "altcolor.ini", NULL);
284 if (!g_file_test (tmp, G_FILE_TEST_EXISTS))
285 {
286 g_free (tmp);
287 tmp = g_build_filename ("/etc/klavaro/altcolor.ini", NULL);
288 }
289 if (!g_file_test (tmp, G_FILE_TEST_EXISTS))
290 {
291 g_free (tmp);
292 tmp = g_build_filename (main_path_data (), "altcolor.ini", NULL);
293 }
294 g_key_file_load_from_file (altcolor, tmp, G_KEY_FILE_NONE, NULL);
295 g_free (tmp);
296
297 /* Other initializations
298 */
299 trans_init_lang_name_code ();
300 trans_init_language_env ();
301 srand (time (0));
302 tutor_init_timers ();
303
304 KEYB_CUSTOM = g_strdup (_("(Custom)"));
305 KEYB_EDIT = g_strdup (_("(Edit custom)"));
306 OTHER_DEFAULT = g_strdup (_("(Default)"));
307 }
308
309 static void
main_gtkbuilder_translation_workaround()310 main_gtkbuilder_translation_workaround ()
311 {
312 gchar *lb;
313 GSList *ls;
314 GSList *it;
315 GtkWidget *wg;
316
317 ls = gtk_builder_get_objects (gui);
318 it = ls;
319 do
320 {
321 if (G_OBJECT_TYPE (it->data) == GTK_TYPE_LABEL)
322 {
323 lb = (gchar *) gtk_label_get_label (GTK_LABEL (it->data));
324 if (lb[0] != '\0')
325 gtk_label_set_text_with_mnemonic (GTK_LABEL (it->data), _(lb));
326 }
327 else if (G_OBJECT_TYPE (it->data) == GTK_TYPE_ENTRY)
328 {
329 lb = gtk_entry_get_icon_tooltip_text (GTK_ENTRY (it->data), GTK_ENTRY_ICON_SECONDARY);
330 if (lb)
331 {
332 if (lb[0] != '\0')
333 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (it->data), GTK_ENTRY_ICON_SECONDARY, _(lb));
334 }
335 }
336 else if (G_OBJECT_TYPE (it->data) == GTK_TYPE_WINDOW)
337 gtk_window_set_title (GTK_WINDOW (it->data), _(gtk_window_get_title (GTK_WINDOW (it->data))));
338
339 if (GTK_IS_WIDGET (it->data))
340 {
341 wg = GTK_WIDGET (it->data);
342 if (gtk_widget_get_has_tooltip (wg))
343 gtk_widget_set_tooltip_text (wg, _(gtk_widget_get_tooltip_text (wg)));
344 }
345
346 it = g_slist_next (it);
347 } while (it);
348
349 g_slist_free (ls);
350 }
351
352 /*******************************************************************************
353 * Initialize some interface widgets
354 */
355 static void
main_window_init()356 main_window_init ()
357 {
358 gchar *tmp = NULL;
359 gchar *ttip = NULL;
360 PangoAttrList *palist;
361
362 /* Workaround to make GtkBuilder translate all text, on Windows :-(
363 */
364 if (! UNIX_OK)
365 main_gtkbuilder_translation_workaround ();
366
367 /* Set the language
368 */
369 trans_set_combo_language ();
370
371 /* Set keyboard
372 */
373 keyb_create_virtual_keys ();
374 hints_init ();
375
376 /* Set if speech is enabled
377 */
378 callbacks_shield_set (TRUE);
379 if (!main_preferences_exist ("interface", "speech"))
380 main_preferences_set_boolean ("interface", "speech", TRUE);
381 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (get_wg ("checkbutton_speech")),
382 main_preferences_get_boolean ("interface", "speech"));
383 tmp = g_strdup (dngettext (PACKAGE, "Dictation mode (depends on this speech synthesizer: %s)",
384 "Dictation mode (depends on one of these speech synthesizers: %s)", 1));
385 ttip = g_strdup_printf (tmp, "Espeak");
386 g_free (tmp);
387 gtk_widget_set_tooltip_text (get_wg ("checkbutton_speech"), ttip);
388 callbacks_shield_set (FALSE);
389
390 /* Set the initial keyboard to use
391 */
392 keyb_set_combo_kbd ("combobox_kbd_country", "combobox_kbd_variant");
393 keyb_set_combo_kbd ("combobox_keyboard_country", "combobox_keyboard_variant");
394
395 /* Set window icons
396 */
397 gtk_window_set_default_icon_name ("klavaro");
398
399 /* Set pixmaps
400 */
401 set_pixmap ("image_altcolor", "altcolor.png");
402 set_pixmap ("image_fluid", "fluid.png");
403 set_pixmap ("image_keyboard", "key.png");
404 set_pixmap ("image_other", "other.png");
405 set_pixmap ("image_progress", "progress.png");
406 set_pixmap ("image_top10", "top10.png");
407
408 /* Set Top10 TreeViews and Combo
409 */
410 top10_init ();
411
412 /* Set big and bold labels
413 */
414 pango_parse_markup ("<big><b>----------------------------------------------------------------</b></big>",
415 -1, 0, &palist, NULL, NULL, NULL);
416 gtk_label_set_attributes (GTK_LABEL (get_wg ("label_keyboard_title")), palist);
417 gtk_label_set_attributes (GTK_LABEL (get_wg ("label_main_intro")), palist);
418 gtk_label_set_attributes (GTK_LABEL (get_wg ("label_main_basic")), palist);
419 gtk_label_set_attributes (GTK_LABEL (get_wg ("label_main_adapt")), palist);
420 gtk_label_set_attributes (GTK_LABEL (get_wg ("label_main_velo")), palist);
421 gtk_label_set_attributes (GTK_LABEL (get_wg ("label_main_fluid")), palist);
422
423 /* Set main labels (for translation)
424 */
425 tmp = g_strdup_printf ("0 - %s", _("Introduction"));
426 gtk_label_set_text (GTK_LABEL (get_wg ("label_main_intro")), tmp);
427 g_free (tmp);
428 tmp = g_strdup_printf ("1 - %s", _("Basic course"));
429 gtk_label_set_text (GTK_LABEL (get_wg ("label_main_basic")), tmp);
430 g_free (tmp);
431 tmp = g_strdup_printf ("2 - %s", _("Adaptability"));
432 gtk_label_set_text (GTK_LABEL (get_wg ("label_main_adapt")), tmp);
433 g_free (tmp);
434 if (velo_txt)
435 tmp = g_strdup_printf ("3 - %s (TXT)", _("Speed"));
436 else
437 tmp = g_strdup_printf ("3 - %s", _("Speed"));
438 gtk_label_set_text (GTK_LABEL (get_wg ("label_main_velo")), tmp);
439 g_free (tmp);
440 tmp = g_strdup_printf ("4 - %s", _("Fluidity"));
441 gtk_label_set_text (GTK_LABEL (get_wg ("label_main_fluid")), tmp);
442 g_free (tmp);
443
444 /* Set version
445 */
446 gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (get_obj ("aboutdialog_klavaro")), VERSION);
447
448 /* For remembering the window's position
449 */
450 gtk_widget_show (get_wg ("window_main"));
451 window_restore ("main");
452
453 /* Altcolor toggle button
454 */
455 if (!main_altcolor_exist ("colors", "altcolor"))
456 main_altcolor_set_boolean ("colors", "altcolor", FALSE);
457 if (main_altcolor_get_boolean ("colors", "altcolor") == TRUE)
458 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (get_wg ("togglebutton_altcolor")), TRUE);
459
460 /* Run last used module
461 */
462 if (!main_preferences_exist ("interface", "autostart"))
463 main_preferences_set_boolean ("interface", "autostart", FALSE);
464 if (!main_preferences_exist ("interface", "exercise"))
465 main_preferences_set_int ("interface", "exercise", 1);
466 if (main_preferences_get_boolean ("interface", "autostart"))
467 {
468 switch (main_preferences_get_int ("interface", "exercise"))
469 {
470 case TT_BASIC:
471 on_button_basic_clicked (NULL, NULL);
472 break;
473 case TT_ADAPT:
474 on_button_adapt_clicked (NULL, NULL);
475 break;
476 case TT_VELO:
477 on_button_velo_clicked (NULL, NULL);
478 break;
479 case TT_FLUID:
480 on_button_fluid_clicked (NULL, NULL);
481 break;
482 }
483 }
484
485 /* Set accuracy accumulators
486 */
487 accur_init ();
488 }
489
490 /*******************************************************************************
491 * Main program
492 */
493 int
main(int argc,char * argv[])494 main (int argc, char *argv[])
495 {
496 gchar *tmp;
497 gboolean success = FALSE;
498 gboolean show_version = FALSE;
499 GOptionContext *opct;
500 GOptionEntry option[] = {
501 {"version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Version", NULL},
502 {"velotxt", 'x', 0, G_OPTION_ARG_NONE, &velo_txt, "Speed TXT", NULL},
503 {NULL}
504 };
505 GError *gerr = NULL;
506
507 /* Localization
508 */
509 #ifdef ENABLE_NLS
510 bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
511 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
512 textdomain (GETTEXT_PACKAGE);
513 #endif
514
515 /* Command-line arguments
516 */
517 opct = g_option_context_new ("\t");
518 g_option_context_set_translation_domain (opct, GETTEXT_PACKAGE);
519 g_option_context_add_main_entries (opct, option, GETTEXT_PACKAGE);
520 g_option_context_add_group (opct, gtk_get_option_group (TRUE));
521 g_setenv ("NO_AT_BRIDGE", "1", FALSE); /* to eliminate annoying accessibility bus warning */
522 if (g_option_context_parse (opct, &argc, &argv, &gerr) == FALSE)
523 {
524 g_printf ("%s\n", gerr->message);
525 g_printf ("klavaro -h\n");
526 return 0;
527 }
528
529 if (show_version)
530 {
531 g_printf (VERSION"\n");
532 //g_printf (PACKAGE_LOCALE_DIR"\n");
533 return 0;
534 }
535
536 curl_ok = curl_global_init (CURL_GLOBAL_WIN32) == CURLE_OK ? TRUE : FALSE;
537
538 main_initialize_global_variables (); /* Here the locale is got. */
539
540 /* Create all the interface stuff
541 */
542 gui = gtk_builder_new ();
543 gtk_builder_set_translation_domain (gui, NULL);
544
545 tmp = g_build_filename (main_path_data (), "klavaro.glade", NULL);
546 if (g_file_test (tmp, G_FILE_TEST_IS_REGULAR))
547 success = gtk_builder_add_from_file (gui, tmp, NULL);
548 else
549 g_error ("GUI file not found. Aborting.\n %s", tmp);
550 if (!success)
551 g_error ("GUI file found but couldn't create the GUI. Aborting.");
552 if (!g_module_supported ())
553 g_error ("GUI created but can't connect signals.");
554 g_free (tmp);
555 gtk_builder_connect_signals (gui, NULL);
556
557 main_window_init (); /* and initialize its parameters */
558
559 gtk_main ();
560
561 return 0;
562 }
563
564 /*******************************************************************************
565 * Quit the application
566 */
567 void
main_window_pass_away()568 main_window_pass_away ()
569 {
570 main_preferences_save ();
571 accur_close ();
572 g_rmdir ("tmp/klavaro");
573 if (curl_ok) curl_global_cleanup ();
574 g_print ("\nAdiaux!\n");
575 exit (0);
576 }
577