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