1 /*
2  * lingot, a musical instrument tuner.
3  *
4  * Copyright (C) 2004-2018  Iban Cereijo.
5  * Copyright (C) 2004-2008  Jairo Chapela.
6 
7  *
8  * This file is part of lingot.
9  *
10  * lingot is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * lingot is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with lingot; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28 
29 #include "lingot-gui-config-dialog.h"
30 #include "lingot-gui-config-dialog-scale.h"
31 #include "lingot-msg.h"
32 #include "lingot-i18n.h"
33 
34 enum {
35 	COLUMN_NAME = 0, COLUMN_SHIFT = 1, COLUMN_FREQUENCY = 2, NUM_COLUMNS = 3
36 };
37 
lingot_gui_config_dialog_scale_callback_change_deviation(GtkWidget * widget,LingotConfigDialog * dialog)38 void lingot_gui_config_dialog_scale_callback_change_deviation(GtkWidget *widget,
39 		LingotConfigDialog *dialog) {
40 	(void)widget;           //  Unused parameter.
41 	gtk_widget_queue_draw(GTK_WIDGET(dialog->scale_treeview));
42 }
43 
lingot_gui_config_dialog_scale_callback_change_octave(GtkWidget * widget,LingotConfigDialog * dialog)44 void lingot_gui_config_dialog_scale_callback_change_octave(GtkWidget *widget,
45 		LingotConfigDialog *dialog) {
46 	(void)widget;           //  Unused parameter.
47 	gtk_widget_queue_draw(GTK_WIDGET(dialog->scale_treeview));
48 }
49 
lingot_gui_config_dialog_scale_tree_add_row_tree(gpointer data,GtkTreeView * treeview)50 void lingot_gui_config_dialog_scale_tree_add_row_tree(gpointer data,
51 		GtkTreeView *treeview) {
52 	(void)data;             //  Unused parameter.
53 	GtkTreeModel *model;
54 	GtkTreeStore *model_store;
55 	GtkTreeIter iter1, iter2;
56 	gdouble freq;
57 
58 	model = gtk_tree_view_get_model(treeview);
59 	model_store = (GtkTreeStore *) model;
60 
61 	GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
62 
63 	if (gtk_tree_selection_count_selected_rows(selection) == 0) {
64 		gtk_tree_store_append(model_store, &iter2, NULL);
65 	} else {
66 		GList *list = gtk_tree_selection_get_selected_rows(selection, &model);
67 
68 		int ipath = atoi(gtk_tree_path_to_string(list->data));
69 		GString *fixed_path = g_string_new("");
70 		g_string_printf(fixed_path, "%d", ipath);
71 
72 		GtkTreePath *path = gtk_tree_path_new_from_string(fixed_path->str);
73 		g_string_free(fixed_path, TRUE);
74 
75 		gboolean valid = FALSE;
76 
77 		if (path) {
78 			if (gtk_tree_model_get_iter(model, &iter1, path)) { // get iter from specified path
79 				valid = TRUE;
80 			} else { // invalid path
81 				//			g_error("Error!!!\n");
82 				// TODO
83 			}
84 			gtk_tree_path_free(path);
85 		} else {
86 			//		g_error("Error!!!\n");
87 			// TODO
88 		}
89 
90 		g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
91 		g_list_free(list);
92 
93 		if (!valid)
94 			return;
95 
96 		if (ipath == 0) {
97 			lingot_msg_add_warning(
98 					_("You cannot insert before the reference note."));
99 			return;
100 		}
101 
102 		//	g_free(path_str);
103 		gtk_tree_store_insert_before(model_store, &iter2, NULL, &iter1);
104 	}
105 
106 	gtk_tree_model_get_iter_first(model, &iter1);
107 	gtk_tree_model_get(model, &iter1, COLUMN_FREQUENCY, &freq, -1);
108 
109 	gtk_tree_store_set(model_store, &iter2, COLUMN_NAME, "?", COLUMN_SHIFT,
110 			"1/1", COLUMN_FREQUENCY, freq, -1);
111 }
112 
lingot_gui_config_dialog_scale_tree_remove_selected_items(gpointer data,GtkTreeView * treeview)113 void lingot_gui_config_dialog_scale_tree_remove_selected_items(gpointer data,
114 		GtkTreeView *treeview) {
115 	(void)data;             //  Unused parameter.
116 	GtkTreeStore *store;
117 	GtkTreeModel *model;
118 	GtkTreeIter iter;
119 	GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
120 
121 	if (gtk_tree_selection_count_selected_rows(selection) == 0)
122 		return;
123 
124 	GList *list = gtk_tree_selection_get_selected_rows(selection, &model);
125 	store = GTK_TREE_STORE(model);
126 
127 	int nRemoved = 0;
128 	while (list) {
129 		int ipath = atoi(gtk_tree_path_to_string(list->data));
130 		ipath -= nRemoved;
131 		GString *fixed_path = g_string_new("");
132 		g_string_printf(fixed_path, "%d", ipath);
133 
134 		GtkTreePath *path = gtk_tree_path_new_from_string(fixed_path->str);
135 		g_string_free(fixed_path, TRUE);
136 		if (path) {
137 			if (ipath != 0) {
138 				if (gtk_tree_model_get_iter(model, &iter, path)) { // get iter from specified path
139 					gtk_tree_store_remove(store, &iter); // remove item
140 					nRemoved++;
141 				} else { // invalid path
142 					//			g_error("Error!!!\n");
143 					// TODO
144 				}
145 			}
146 			gtk_tree_path_free(path);
147 		} else {
148 			//		g_error("Error!!!\n");
149 			// TODO
150 		}
151 		list = list->next;
152 	}
153 	g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
154 	g_list_free(list);
155 }
156 
lingot_gui_config_dialog_scale_tree_cell_edited_callback(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,gpointer user_data)157 void lingot_gui_config_dialog_scale_tree_cell_edited_callback(
158 		GtkCellRendererText *cell, gchar *path_string, gchar *new_text,
159 		gpointer user_data) {
160 	(void)path_string;      //  Unused parameter.
161 	GtkTreeView *treeview;
162 	GtkTreeModel *model;
163 	GtkTreeStore *model_store;
164 	GtkTreeIter iter, iter2;
165 	GtkCellRenderer *renderer;
166 	char* shift_char;
167 	char* stored_shift_char;
168 	gdouble freq, stored_freq, base_freq, stored_shift;
169 	double shift_cents;
170 	short int shift_numerator, shift_denominator;
171 	gdouble shiftf2, freq2;
172 	char* char_pointer;
173 	char buff[512];
174 	const char* delim = " \t\n";
175 	LingotConfigDialog* config_dialog = (LingotConfigDialog*) user_data;
176 	int index;
177 
178 	treeview = config_dialog->scale_treeview;
179 	model_store = (GtkTreeStore *) gtk_tree_view_get_model(treeview);
180 
181 	GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
182 
183 	if (gtk_tree_selection_count_selected_rows(selection) != 1)
184 		return;
185 
186 	GList *list = gtk_tree_selection_get_selected_rows(selection, &model);
187 	//	model_store = GTK_TREE_STORE ( model );
188 
189 	int ipath = atoi(gtk_tree_path_to_string(list->data));
190 	GString *fixed_path = g_string_new("");
191 	g_string_printf(fixed_path, "%d", ipath);
192 
193 	GtkTreePath *path = gtk_tree_path_new_from_string(fixed_path->str);
194 	g_string_free(fixed_path, TRUE);
195 
196 	gboolean valid = FALSE;
197 
198 	if (path) {
199 		if (gtk_tree_model_get_iter(model, &iter, path)) { // get iter from specified path
200 			valid = TRUE;
201 		} else { // invalid path
202 			//			g_error("Error!!!\n");
203 			// TODO
204 		}
205 		gtk_tree_path_free(path);
206 	} else {
207 		//		g_error("Error!!!\n");
208 		// TODO
209 	}
210 
211 	g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
212 	g_list_free(list);
213 
214 	if (!valid)
215 		return;
216 
217 	renderer = &cell->parent;
218 	guint column_number = GPOINTER_TO_UINT(
219 			g_object_get_data( G_OBJECT(renderer), "my_column_num"));
220 
221 	switch (column_number) {
222 
223 	case COLUMN_NAME: {
224 		if (strchr(new_text, ' ') || strchr(new_text, '\t')) {
225 			lingot_msg_add_warning(
226 					_("Do not use space characters for the note names."));
227 		} else if (strchr(new_text, '\n') || strchr(new_text, '{')
228 				|| strchr(new_text, '}')) {
229 			lingot_msg_add_warning(_("The name contains illegal characters."));
230 		} else {
231 			char_pointer = strtok(new_text, delim);
232 			gtk_tree_store_set(model_store, &iter, COLUMN_NAME,
233 					(char_pointer == NULL) ? "?" : new_text, -1);
234 		}
235 	}
236 		break;
237 
238 	case COLUMN_SHIFT: {
239 		lingot_config_scale_parse_shift(new_text, &shift_cents,
240 				&shift_numerator, &shift_denominator);
241 
242 		// TODO: full validation
243 
244 		if ((ipath == 0) && (fabs(shift_cents - 0.0) > 1e-10)) {
245 			lingot_msg_add_warning(
246 					_("You cannot change the first shift, it must be 1/1."));
247 			break;
248 		}
249 
250 		if (isnan(shift_cents) || (shift_cents <= 0.0 - 1e-10)
251 				|| (shift_cents > 1200.0)) {
252 			lingot_msg_add_warning(
253 					_(
254 							"The shift must be between 0 and 1200 cents, or between 1/1 and 2/1."));
255 			break;
256 		}
257 
258 		gtk_tree_model_get(model, &iter, COLUMN_SHIFT, &stored_shift_char,
259 				COLUMN_FREQUENCY, &stored_freq, -1);
260 		lingot_config_scale_parse_shift(stored_shift_char, &stored_shift, NULL,
261 		NULL);
262 		free(stored_shift_char);
263 		lingot_config_scale_format_shift(buff, shift_cents, shift_numerator,
264 				shift_denominator);
265 		gtk_tree_store_set(model_store, &iter, COLUMN_SHIFT, buff,
266 				COLUMN_FREQUENCY,
267 				stored_freq * pow(2.0, (shift_cents - stored_shift) / 1200.0),
268 				-1);
269 	}
270 		break;
271 
272 	case COLUMN_FREQUENCY: {
273 		if (!strcmp(new_text, _("mid-A"))) {
274 			freq = MID_A_FREQUENCY;
275 		} else if (!strcmp(new_text, _("mid-C"))) {
276 			freq = MID_C_FREQUENCY;
277 		} else {
278 			sscanf(new_text, "%lg", &freq);
279 			// TODO: validation
280 		}
281 
282 		gtk_tree_model_get(model, &iter, COLUMN_SHIFT, &shift_char,
283 				COLUMN_FREQUENCY, &stored_freq, -1);
284 		lingot_config_scale_parse_shift(shift_char, &shift_cents, NULL, NULL);
285 		free(shift_char);
286 
287 		freq *= pow(2.0,
288 				-gtk_spin_button_get_value(config_dialog->root_frequency_error)
289 						/ 1200.0
290 						- (gtk_combo_box_get_active(
291 								GTK_COMBO_BOX(config_dialog->octave)) - 4));
292 		base_freq = freq * pow(2.0, -shift_cents / 1200.0);
293 
294 		gtk_tree_model_get_iter_first(model, &iter2);
295 
296 		index = 0;
297 		do {
298 			gtk_tree_model_get(model, &iter2, COLUMN_SHIFT, &shift_char, -1);
299 			lingot_config_scale_parse_shift(shift_char, &shiftf2, NULL, NULL);
300 			free(shift_char);
301 			freq2 = base_freq * pow(2.0, shiftf2 / 1200.0);
302 			gtk_tree_store_set(model_store, &iter2, COLUMN_FREQUENCY,
303 					(index == ipath) ? freq : freq2, -1);
304 			index++;
305 		} while (gtk_tree_model_iter_next(model, &iter2));
306 
307 		//gtk_spin_button_set_value(config_dialog->root_frequency_error, 0.0);
308 	}
309 		break;
310 	}
311 
312 }
313 
lingot_gui_config_dialog_scale_tree_frequency_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)314 void lingot_gui_config_dialog_scale_tree_frequency_cell_data_function(
315 		GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model,
316 		GtkTreeIter *iter, gpointer user_data) {
317 	(void)col;              //  Unused parameter.
318 	gdouble freq;
319 	gchar buf[20];
320 	LingotConfigDialog* config_dialog = (LingotConfigDialog*) user_data;
321 
322 	gtk_tree_model_get(model, iter, COLUMN_FREQUENCY, &freq, -1);
323 	freq *= pow(2.0,
324 			gtk_spin_button_get_value(config_dialog->root_frequency_error)
325 					/ 1200.0
326 					+ (gtk_combo_box_get_active(
327 							GTK_COMBO_BOX(config_dialog->octave)) - 4));
328 
329 	if (fabs(freq - MID_A_FREQUENCY) < 1e-3) {
330 		g_object_set(renderer, "text", _("mid-A"), NULL);
331 	} else if (fabs(freq - MID_C_FREQUENCY) < 1e-3) {
332 		g_object_set(renderer, "text", _("mid-C"), NULL);
333 	} else {
334 		g_snprintf(buf, sizeof(buf), "%.4f", freq);
335 		g_object_set(renderer, "text", buf, NULL);
336 	}
337 }
338 
lingot_gui_config_dialog_scale_tree_add_column(LingotConfigDialog * config_dialog)339 void lingot_gui_config_dialog_scale_tree_add_column(
340 		LingotConfigDialog* config_dialog) {
341 	GtkCellRenderer *renderer;
342 	GtkTreeViewColumn *column;
343 	GtkTreeView *treeview = config_dialog->scale_treeview;
344 
345 	column = gtk_tree_view_column_new();
346 
347 	gtk_tree_view_column_set_title(column, _("Name"));
348 
349 	renderer = gtk_cell_renderer_text_new();
350 	gtk_tree_view_column_pack_start(column, renderer, FALSE);
351 	gtk_tree_view_column_set_attributes(column, renderer, "text", COLUMN_NAME,
352 	NULL);
353 	g_object_set(renderer, "editable", TRUE, NULL);
354 	g_object_set_data(G_OBJECT(renderer), "my_column_num",
355 			GUINT_TO_POINTER(COLUMN_NAME));
356 	g_signal_connect(renderer, "edited",
357 			(GCallback ) lingot_gui_config_dialog_scale_tree_cell_edited_callback,
358 			config_dialog);
359 
360 	gtk_tree_view_append_column(treeview, column);
361 	column = gtk_tree_view_column_new();
362 	gtk_tree_view_column_set_title(column, _("Shift"));
363 
364 	renderer = gtk_cell_renderer_text_new();
365 	gtk_tree_view_column_pack_start(column, renderer, FALSE);
366 	gtk_tree_view_column_set_attributes(column, renderer, "text", COLUMN_SHIFT,
367 	NULL);
368 	g_object_set(renderer, "editable", TRUE, NULL);
369 	g_object_set_data(G_OBJECT(renderer), "my_column_num",
370 			GUINT_TO_POINTER(COLUMN_SHIFT));
371 	//	gtk_tree_view_column_set_cell_data_func(column, renderer,
372 	//			lingot_config_dialog_scale_tree_shift_cell_data_function, NULL,
373 	//			NULL);
374 
375 	g_signal_connect(renderer, "edited",
376 			(GCallback ) lingot_gui_config_dialog_scale_tree_cell_edited_callback,
377 			config_dialog);
378 
379 	gtk_tree_view_append_column(treeview, column);
380 	column = gtk_tree_view_column_new();
381 	gtk_tree_view_column_set_title(column, _("Frequency [Hz]"));
382 
383 	renderer = gtk_cell_renderer_text_new();
384 	gtk_tree_view_column_pack_start(column, renderer, FALSE);
385 	gtk_tree_view_column_set_attributes(column, renderer, "text",
386 			COLUMN_FREQUENCY, NULL);
387 	g_object_set(renderer, "editable", TRUE, NULL);
388 	g_object_set_data(G_OBJECT(renderer), "my_column_num",
389 			GUINT_TO_POINTER(COLUMN_FREQUENCY));
390 	gtk_tree_view_column_set_cell_data_func(column, renderer,
391 			lingot_gui_config_dialog_scale_tree_frequency_cell_data_function,
392 			config_dialog, NULL);
393 
394 	g_signal_connect(renderer, "edited",
395 			(GCallback ) lingot_gui_config_dialog_scale_tree_cell_edited_callback,
396 			config_dialog);
397 
398 	gtk_tree_view_append_column(treeview, column);
399 }
400 
lingot_gui_config_dialog_scale_validate(const LingotConfigDialog * dialog,const LingotScale * scale)401 int lingot_gui_config_dialog_scale_validate(const LingotConfigDialog* dialog, const LingotScale* scale) {
402 	(void)scale;            //  Unused parameter.
403 	GtkTreeIter iter, iter2;
404 	GtkTreeModel* model = gtk_tree_view_get_model(dialog->scale_treeview);
405 
406 	char* name;
407 	char* name2;
408 	char* shift_char;
409 	gdouble shift, last_shift;
410 	int empty_names = 0;
411 
412 	gtk_tree_model_get_iter_first(model, &iter);
413 
414 	last_shift = -1.0;
415 
416 	// TODO: full validation
417 
418 	int row1 = 0;
419 
420 	do {
421 		gtk_tree_model_get(model, &iter, COLUMN_NAME, &name, COLUMN_SHIFT, &shift_char, -1);
422 		lingot_config_scale_parse_shift(shift_char, &shift, NULL, NULL);
423 		free(shift_char);
424 
425 		if (!strcmp(name, "") || !strcmp(name, "?")) {
426 			empty_names = 1;
427 		}
428 
429 		gtk_tree_model_get_iter_first(model, &iter2);
430 		int row2 = 0;
431 		do {
432 			gtk_tree_model_get(model, &iter2, COLUMN_NAME, &name2, -1);
433 			if ((row1 != row2) && !strcmp(name, name2)) {
434 				lingot_msg_add_error(_("There are notes with the same name"));
435 				// TODO: select the conflictive line
436 				free(name);
437 				free(name2);
438 				return 0;
439 			}
440 			free(name2);
441 			row2++;
442 		} while (gtk_tree_model_iter_next(model, &iter2));
443 
444 		free(name);
445 
446 		if (shift < last_shift) {
447 			lingot_msg_add_error(
448 					_("There are invalid values in the scale: the notes should be ordered by frequency / shift"));
449 			// TODO: select the conflictive line
450 			return 0;
451 		}
452 
453 		if (shift >= 1200.0) {
454 			lingot_msg_add_error(
455 					_("There are invalid values in the scale: all the notes should be in the same octave"));
456 			return 0;
457 		}
458 
459 		last_shift = shift;
460 		row1++;
461 	} while (gtk_tree_model_iter_next(model, &iter));
462 
463 	if (empty_names) {
464 		lingot_msg_add_warning(_("There are notes without name"));
465 		return 0;
466 	}
467 
468 	return 1;
469 }
470 
lingot_gui_config_dialog_scale_gui_to_data(const LingotConfigDialog * dialog,LingotScale * scale)471 void lingot_gui_config_dialog_scale_gui_to_data(const LingotConfigDialog* dialog, LingotScale* scale) {
472 
473 	GtkTreeIter iter;
474 	GtkTreeModel* model = gtk_tree_view_get_model(dialog->scale_treeview);
475 	gdouble freq, shift;
476 	short int shift_num, shift_den;
477 	char* shift_char;
478 	gchar* name;
479 	int i = 0;
480 
481 	int rows = gtk_tree_model_iter_n_children(model, NULL);
482 
483 	gtk_tree_model_get_iter_first(model, &iter);
484 	gtk_tree_model_get(model, &iter, COLUMN_FREQUENCY, &freq, -1);
485 
486 	scale->name = strdup(gtk_entry_get_text(dialog->scale_name));
487 	scale->base_frequency = freq;
488 	lingot_config_scale_allocate(scale, rows);
489 
490 	do {
491 		gtk_tree_model_get(model, &iter, COLUMN_NAME, &name, COLUMN_SHIFT, &shift_char, -1);
492 		lingot_config_scale_parse_shift(shift_char, &shift, &shift_num, &shift_den);
493 		free(shift_char);
494 
495 		scale->note_name[i] = name;
496 		scale->offset_cents[i] = shift;
497 		scale->offset_ratios[0][i] = shift_num;
498 		scale->offset_ratios[1][i] = shift_den;
499 		i++;
500 	} while (gtk_tree_model_iter_next(model, &iter));
501 }
502 
lingot_gui_config_dialog_scale_data_to_gui(LingotConfigDialog * dialog,const LingotScale * scale)503 void lingot_gui_config_dialog_scale_data_to_gui(LingotConfigDialog* dialog, const LingotScale* scale) {
504 	gtk_entry_set_text(dialog->scale_name, scale->name);
505 	GtkTreeStore* store = (GtkTreeStore *) gtk_tree_view_get_model(
506 			dialog->scale_treeview);
507 	gtk_tree_store_clear(store);
508 	GtkTreeIter iter2;
509 	char buff[512];
510 
511 	int i;
512 	for (i = 0; i < scale->notes; i++) {
513 		gtk_tree_store_append(store, &iter2, NULL);
514 		FLT freq = scale->base_frequency
515 				* pow(2.0, scale->offset_cents[i] / 1200.0);
516 		lingot_config_scale_format_shift(buff, scale->offset_cents[i],
517 				scale->offset_ratios[0][i], scale->offset_ratios[1][i]);
518 		gtk_tree_store_set(store, &iter2, COLUMN_NAME, scale->note_name[i],
519 				COLUMN_SHIFT, buff, COLUMN_FREQUENCY, freq, -1);
520 	}
521 }
522 
lingot_gui_config_dialog_import_scl(gpointer data,LingotConfigDialog * config_dialog)523 void lingot_gui_config_dialog_import_scl(gpointer data,
524 		LingotConfigDialog* config_dialog) {
525 	(void)data;             //  Unused parameter.
526 	GtkWidget * dialog = gtk_file_chooser_dialog_new(_("Open Scale File"),
527 			GTK_WINDOW(config_dialog->win), GTK_FILE_CHOOSER_ACTION_OPEN,
528 			"_Cancel", GTK_RESPONSE_CANCEL, "_Open",
529 			GTK_RESPONSE_ACCEPT, NULL);
530 	GtkFileFilter *filefilter;
531 	filefilter = gtk_file_filter_new();
532 	static gchar* filechooser_last_folder = NULL;
533 
534 	gtk_file_filter_set_name(filefilter, (const gchar *) _("Scala files"));
535 	gtk_file_filter_add_pattern(filefilter, "*.scl");
536 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filefilter);
537 
538 	if (filechooser_last_folder != NULL) {
539 		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
540 				filechooser_last_folder);
541 	}
542 
543 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
544 		char *filename;
545 		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
546 		if (filechooser_last_folder != NULL) {
547 			free(filechooser_last_folder);
548 			filechooser_last_folder = NULL;
549 		}
550 
551 		if (gtk_file_chooser_get_current_folder(
552 				GTK_FILE_CHOOSER(dialog)) != NULL) {
553 			filechooser_last_folder = strdup(
554 					gtk_file_chooser_get_current_folder(
555 							GTK_FILE_CHOOSER(dialog)));
556 		}
557 
558 		// TODO
559 		LingotScale scale;
560                 lingot_config_scale_new(&scale);
561 		if (lingot_config_scale_load_scl(&scale, filename)) {
562 			lingot_gui_config_dialog_scale_data_to_gui(config_dialog, &scale);
563 		}
564 
565 		lingot_config_scale_destroy(&scale);
566 		g_free(filename);
567 	}
568 	gtk_widget_destroy(dialog);
569 	//g_free(filefilter);
570 }
571 
lingot_gui_config_dialog_scale_key_press_cb(GtkWidget * widget,GdkEventKey * kevent,gpointer data)572 gint lingot_gui_config_dialog_scale_key_press_cb(GtkWidget *widget,
573 		GdkEventKey *kevent, gpointer data) {
574 	(void)widget;           //  Unused parameter.
575 
576 	LingotConfigDialog* dialog = (LingotConfigDialog*) data;
577 	if (kevent->type == GDK_KEY_PRESS) {
578 		if (kevent->keyval == 0xffff) {
579 			g_signal_emit_by_name(G_OBJECT(dialog->button_scale_del), "clicked",
580 			NULL);
581 		} else if (kevent->keyval == 0xff63) {
582 			g_signal_emit_by_name(G_OBJECT(dialog->button_scale_add), "clicked",
583 			NULL);
584 		}
585 	}
586 
587 	return TRUE;
588 }
589 
lingot_gui_config_dialog_scale_tree_view_column_get_index(GtkTreeViewColumn * column)590 gint lingot_gui_config_dialog_scale_tree_view_column_get_index(
591 		GtkTreeViewColumn *column) {
592 	GtkTreeView *tree = GTK_TREE_VIEW(
593 			gtk_tree_view_column_get_tree_view(column));
594 	GList *cols = gtk_tree_view_get_columns(tree);
595 	int counter = 0;
596 
597 	while (cols != NULL) {
598 		if (column == GTK_TREE_VIEW_COLUMN(cols->data)) {
599 			g_list_free(cols);
600 			return counter;
601 		}
602 		cols = cols->next;
603 		counter++;
604 	}
605 
606 	g_list_free(cols);
607 	return -1;
608 }
609 
lingot_gui_config_dialog_scale_table_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)610 gboolean lingot_gui_config_dialog_scale_table_query_tooltip(GtkWidget *widget,
611 		gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip,
612 		gpointer user_data) {
613 	(void)keyboard_mode;    //  Unused parameter.
614 	(void)user_data;        //  Unused parameter.
615 
616 	GtkTreePath* path;
617 	GtkTreeViewColumn* col;
618 	gint cx, cy;
619 
620 	gtk_tree_view_get_path_at_pos((GtkTreeView*) widget, x, y, &path, &col, &cx,
621 			&cy);
622 
623 	gint column_index =
624 			lingot_gui_config_dialog_scale_tree_view_column_get_index(col);
625 
626 	switch (column_index) {
627 	case COLUMN_NAME:
628 		gtk_tooltip_set_text(tooltip,
629 				_(
630 						"Note identifier, free text that will be displayed in the main window when tuning close to the given note. Don't use space characters here."));
631 		break;
632 	case COLUMN_SHIFT:
633 		gtk_tooltip_set_text(tooltip,
634 				_(
635 						"Shift. You can define it as a deviation in cents from the reference note (the first one), or as a frequency ratio, like '3/2' or '5/4'. All the values must be between 0 and 1200 cents, or between 1/1 and 2/1 (i.e., all the notes must be in the same octave), and they must be well ordered."));
636 		break;
637 	case COLUMN_FREQUENCY: {
638 		char buff[600];
639 		snprintf(buff, sizeof(buff),
640 				_(
641 						"Frequency. You can enter here the absolute frequency for a given note as a reference, and all the other frequencies will shift according to the deviations specified in the 'Shift' column. You can either use an absolute numeric value or the keywords '%s' (261.625565 Hz) and '%s' (440 Hz). Those keywords are normally reserved for the 4th octave (try to assign frequencies only to the 4th octave)."),
642 				_("mid-C"), _("mid-A"));
643 		gtk_tooltip_set_text(tooltip, buff);
644 	}
645 		break;
646 	}
647 
648 	return TRUE;
649 }
650 
lingot_gui_config_dialog_scale_show(LingotConfigDialog * dialog,GtkBuilder * builder)651 void lingot_gui_config_dialog_scale_show(LingotConfigDialog* dialog, GtkBuilder* builder) {
652 
653 	dialog->scale_name = GTK_ENTRY(
654 			gtk_builder_get_object(builder, "scale_name"));
655 	GtkWidget* scroll = GTK_WIDGET(
656 			gtk_builder_get_object(builder, "scrolledwindow1"));
657 
658 	/* crea el modelo del arbol */
659 	GtkTreeStore *model_store = gtk_tree_store_new(3, G_TYPE_STRING,
660 	G_TYPE_STRING, G_TYPE_DOUBLE);
661 	GtkTreeModel* model = GTK_TREE_MODEL(model_store);
662 
663 	/* crea un nuevo widget gtktreeview */
664 	dialog->scale_treeview = GTK_TREE_VIEW(gtk_tree_view_new());
665 
666 	/* agrega columnas al modelo del arbol */
667 	lingot_gui_config_dialog_scale_tree_add_column(dialog);
668 
669 	/* asocia el modelo al gtkteeview */
670 	gtk_tree_view_set_model(dialog->scale_treeview, model);
671 	gtk_tree_selection_set_mode(
672 			gtk_tree_view_get_selection(dialog->scale_treeview),
673 			GTK_SELECTION_MULTIPLE);
674 
675 	g_object_unref(G_OBJECT(model));
676 	gtk_container_add(GTK_CONTAINER(scroll),
677 			GTK_WIDGET(dialog->scale_treeview));
678 
679 	dialog->button_scale_del = GTK_BUTTON(
680 			gtk_builder_get_object(builder, "button_scale_del"));
681 	dialog->button_scale_add = GTK_BUTTON(
682 			gtk_builder_get_object(builder, "button_scale_add"));
683 	GtkButton* button_import = GTK_BUTTON(
684 			gtk_builder_get_object(builder, "button_scale_import"));
685 
686 	g_signal_connect(G_OBJECT(dialog->scale_treeview), "key_press_event",
687 			G_CALLBACK(lingot_gui_config_dialog_scale_key_press_cb), dialog);
688 
689 	g_signal_connect(G_OBJECT(dialog->button_scale_add), "clicked",
690 			G_CALLBACK( lingot_gui_config_dialog_scale_tree_add_row_tree),
691 			dialog->scale_treeview);
692 	g_signal_connect(G_OBJECT(dialog->button_scale_del), "clicked",
693 			G_CALLBACK( lingot_gui_config_dialog_scale_tree_remove_selected_items),
694 			dialog->scale_treeview);
695 	g_signal_connect(G_OBJECT(button_import), "clicked",
696 			G_CALLBACK( lingot_gui_config_dialog_import_scl), dialog);
697 
698 	gtk_widget_set_has_tooltip(GTK_WIDGET(dialog->scale_treeview), TRUE);
699 	g_signal_connect(G_OBJECT(dialog->scale_treeview), "query-tooltip",
700 			(GCallback ) lingot_gui_config_dialog_scale_table_query_tooltip,
701 			NULL);
702 }
703