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 /*
15 * Basic course
16 */
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <math.h>
21 #include <glib.h>
22 #include <glib/gstdio.h>
23 #include <gtk/gtk.h>
24
25 #include "auxiliar.h"
26 #include "main.h"
27 #include "keyboard.h"
28 #include "tutor.h"
29 #include "basic.h"
30
31 #define MAX_BASIC_CHAR_SET (8 * 14)
32 struct
33 {
34 gint lesson;
35 gunichar char_set[MAX_BASIC_CHAR_SET];
36 glong char_set_size;
37 gboolean lesson_increased;
38 } basic;
39
40 /*******************************************************************************
41 * Interface functions
42 */
43 gint
basic_get_lesson()44 basic_get_lesson ()
45 {
46 return (basic.lesson);
47 }
48
49 void
basic_set_lesson(gint lesson)50 basic_set_lesson (gint lesson)
51 {
52 if (lesson >= 0)
53 {
54 basic.lesson = lesson;
55 main_preferences_set_int ("tutor", "basic_lesson", lesson);
56 }
57 }
58
59 gunichar *
basic_get_char_set()60 basic_get_char_set ()
61 {
62 return (basic.char_set);
63 }
64
65 gboolean
basic_get_lesson_increased()66 basic_get_lesson_increased ()
67 {
68 return (basic.lesson_increased);
69 }
70
71 void
basic_set_lesson_increased(gboolean state)72 basic_set_lesson_increased (gboolean state)
73 {
74 basic.lesson_increased = state;
75 }
76
77 /*******************************************************************************
78 * Initialize basic vars
79 */
80 void
basic_init()81 basic_init ()
82 {
83 /* Retrieve the last lesson where the student had selected.
84 */
85 basic.lesson_increased = FALSE;
86
87 if (main_preferences_exist ("tutor", "basic_lesson"))
88 basic.lesson = main_preferences_get_int ("tutor", "basic_lesson");
89 else
90 basic_set_lesson (1);
91 basic_init_char_set ();
92 }
93
94 /**********************************************************************
95 * Read the characters to be used with the current basic.lesson
96 */
97 gint
basic_init_char_set()98 basic_init_char_set ()
99 {
100 gint i, j, k;
101 gchar line_str[16];
102 gchar *lesson_file;
103 gchar *lesson_str;
104 gunichar *tmpuc;
105 FILE *fh;
106 GtkWidget *wg;
107
108 /*
109 * Custom lessons
110 */
111 wg = get_wg ("togglebutton_edit_basic_lesson");
112 if (basic.lesson > 43 && basic.lesson <= MAX_BASIC_LESSONS)
113 {
114 lesson_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "basic_lesson_%i.txt",
115 main_path_user (), basic.lesson);
116 if (g_file_get_contents (lesson_file, &lesson_str, NULL, NULL))
117 {
118 tmpuc = g_utf8_to_ucs4_fast (lesson_str, -1, &basic.char_set_size);
119 if (basic.char_set_size > MAX_BASIC_CHAR_SET)
120 basic.char_set_size = MAX_BASIC_CHAR_SET;
121 for (i = j = 0; i < basic.char_set_size; i++)
122 if (g_unichar_isgraph (tmpuc[i]))
123 basic.char_set[j++] = tmpuc[i];
124 basic.char_set[j] = L'\0';
125 basic.char_set_size = j;
126 g_free (tmpuc);
127 g_free (lesson_str);
128 }
129 else
130 {
131 basic.char_set[0] = L' ';
132 basic.char_set[1] = L' ';
133 basic.char_set[2] = L'\0';
134 basic.char_set_size = 2;
135 }
136 g_free (lesson_file);
137
138 gtk_widget_set_sensitive (wg, TRUE);
139 return (-1);
140 }
141 gtk_widget_set_sensitive (wg, FALSE);
142
143 /*
144 * Open the lesson file
145 */
146 lesson_file = g_build_filename (main_path_data (), "basic_lessons.txt", NULL);
147 fh = (FILE *) g_fopen (lesson_file, "r");
148 g_free (lesson_file);
149 if (!fh)
150 g_error ("couldn't find the basic lessons' file.");
151
152 /*
153 * Search the lesson
154 */
155 for (i = 1; i < basic.lesson; i++)
156 for (j = 0; j < 11; j++)
157 if (!(fgets (line_str, 16, fh)))
158 break;
159
160 /*
161 * Pass heading line
162 */
163 if (!(fgets (line_str, 16, fh)))
164 {
165 basic.char_set[0] = L'\0';
166 basic.char_set_size = 0;
167 fclose (fh);
168 return (-1);
169 }
170
171 /*
172 * Get chars, lower set
173 */
174 for (k = 0, i = 0; i < 4; i++)
175 {
176 if (!(fgets (line_str, 16, fh)))
177 {
178 basic.char_set[0] = L'\0';
179 basic.char_set_size = 0;
180 fclose (fh);
181 return (-1);
182 }
183 for (j = 0; j < 14; j++)
184 {
185 if (line_str[j] == '1' && g_unichar_isgraph (keyb_get_lochars (i, j)))
186 basic.char_set[k++] = g_unichar_tolower (keyb_get_lochars (i, j));
187 }
188 }
189
190 /*
191 * Pass blank line
192 */
193 if (!(fgets (line_str, 16, fh)))
194 {
195 basic.char_set[0] = L'\0';
196 basic.char_set_size = 0;
197 fclose (fh);
198 return (-1);
199 }
200
201 /*
202 * Get chars, upper set
203 */
204 for (i = 0; i < 4; i++)
205 {
206 if (!(fgets (line_str, 16, fh)))
207 {
208 basic.char_set[0] = L'\0';
209 basic.char_set_size = 0;
210 fclose (fh);
211 return (-1);
212 }
213 for (j = 0; j < 14; j++)
214 if (line_str[j] == '1' && g_unichar_isgraph (keyb_get_upchars (i, j)))
215 basic.char_set[k++] = g_unichar_tolower (keyb_get_upchars (i, j));
216 }
217 fclose (fh);
218
219 basic.char_set[k] = L'\0';
220 basic.char_set_size = k;
221 return (0);
222 }
223
224 /**********************************************************************
225 * Save the lesson's character set defined in the custom lesson entry,
226 * for the current lesson
227 */
228 void
basic_save_lesson(gchar * charset)229 basic_save_lesson (gchar * charset)
230 {
231 gchar *lesson_file;
232 FILE *fh;
233
234 lesson_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "basic_lesson_%i.txt", main_path_user (), basic.lesson);
235 fh = (FILE *) g_fopen (lesson_file, "w");
236 if (fh == NULL)
237 g_warning ("couldn't save the file:\n %s", lesson_file);
238 else
239 {
240 fprintf (fh, "%s", charset);
241 fclose (fh);
242 if (strlen (charset) < 2)
243 g_unlink (lesson_file);
244 }
245 g_free (lesson_file);
246 }
247
248 /**********************************************************************
249 * Put the lesson's characters at the main screen
250 */
251 #define N_LINES 8
252 void
basic_draw_lesson()253 basic_draw_lesson ()
254 {
255 gint i, j, k, len;
256 gint idx, rnd;
257 gchar *ut8_tmp;
258 gunichar sentence[9 * 6 + 4];
259 gunichar char_pool[2*(N_LINES * 9 * 5)];
260 GtkTextBuffer *buf;
261
262 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (get_wg ("text_tutor")));
263
264 len = basic.char_set_size;
265 if (len < 2)
266 {
267 g_warning ("No character set for this lesson.");
268 return;
269 }
270
271 if (basic.char_set[0] == basic.char_set[1])
272 {
273 ut8_tmp = g_strconcat (_("This custom lesson is empty."
274 " You can define two or more different characters "
275 "to generate a personal practice."
276 " Press the editting button in the upper-right corner."),
277 keyb_get_utf8_paragraph_symbol (), "\n", NULL);
278 gtk_text_buffer_insert_at_cursor (buf, ut8_tmp, -1);
279 gtk_text_buffer_insert_at_cursor (buf, ut8_tmp, -1);
280 gtk_text_buffer_insert_at_cursor (buf, ut8_tmp, -1);
281 gtk_text_buffer_insert_at_cursor (buf, ut8_tmp, -1);
282 g_free (ut8_tmp);
283 return;
284 }
285
286 /*
287 * Draw the lines as sentences
288 */
289 memmove (char_pool, basic.char_set, len * sizeof (gunichar));
290 if (len > 4 && len < 14)
291 {
292 memmove (char_pool+len, basic.char_set, len * sizeof (gunichar));
293 len *= 2;
294 }
295 sentence[9 * 6] = L'\n';
296 sentence[9 * 6 + 1] = L'\0';
297 sentence[9 * 6 + 2] = L'\0';
298 for (i = 0; i < N_LINES; i++)
299 { /* lines (sentences) */
300 idx = 0;
301 for (j = 0; j < 9; j++)
302 { /* words */
303 for (k = 0; k < 5; k++)
304 { /* letters */
305 rnd = rand () % len;
306 sentence[idx++] = char_pool[rnd];
307 len--;
308 char_pool[rnd] = char_pool[len];
309 if (len == 0)
310 {
311 len = basic.char_set_size;
312 memmove (char_pool, basic.char_set, len * sizeof (gunichar));
313 if (len > 4 && len < 14)
314 {
315 memmove (char_pool+len, basic.char_set, len * sizeof (gunichar));
316 len *= 2;
317 }
318 }
319 if (keyb_is_diacritic (sentence[idx-1]))
320 {
321 if (tutor_is_tibetan())
322 {
323 if (k > 0 && keyb_is_vowel(sentence[idx-1]) && keyb_is_vowel(sentence[idx-2]) )
324 {
325 sentence[idx-1] = TIBETAN_WORD_DELIMITER;
326 }
327 }
328 else
329 {
330 sentence[idx-1] = L' ';
331 }
332 }
333 }
334 if (j == 8)
335 sentence[idx] = UPSYM;
336 else
337 sentence[idx] = tutor_is_tibetan() ? TIBETAN_WORD_DELIMITER : L' ';
338 idx++;
339 }
340 ut8_tmp = g_ucs4_to_utf8 (sentence, -1, NULL, NULL, NULL);
341 gtk_text_buffer_insert_at_cursor (buf, ut8_tmp, -1);
342 g_free (ut8_tmp);
343 if (len == 2 && i >= N_LINES/2-1)
344 break;
345 }
346 }
347
348 /**********************************************************************
349 * Put on the screen the final comments
350 */
351 void
basic_comment(gdouble accuracy)352 basic_comment (gdouble accuracy)
353 {
354 gchar *tmp_str;
355 GtkLabel *wg_label;
356 GtkWidget *wg;
357 GtkTextBuffer *buf;
358
359 /*
360 * Comments
361 */
362 if (accuracy < tutor_goal_accuracy ())
363 tmp_str = g_strdup (":-(\n");
364 else
365 {
366 basic_set_lesson (basic.lesson + 1);
367 if (basic.lesson > 43)
368 {
369 if (basic.lesson > MAX_BASIC_LESSONS)
370 basic_set_lesson (MAX_BASIC_LESSONS);
371 wg_label = GTK_LABEL (get_wg ("label_heading"));
372 gtk_label_set_text (wg_label, _("Positions of keys seems to be learned!"));
373 tmp_str = g_strdup (_(" Congratulations!\n"
374 " You have accomplished the entire basic course.\n"
375 " Go to the next type of exercise: adaptability.\n"
376 " There you will practice mainly the accuracy.\n"));
377 }
378 else
379 tmp_str = g_strdup (_(" All right, now you got it!\n Go to the next lesson.\n"));
380
381 basic_init_char_set ();
382 if (basic.lesson != MAX_BASIC_LESSONS)
383 basic.lesson_increased = TRUE;
384 }
385
386 wg = get_wg ("text_tutor");
387 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (wg));
388 gtk_text_buffer_insert_at_cursor (buf, tmp_str, strlen (tmp_str));
389 g_free (tmp_str);
390 }
391