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