1 /*
2 Asunder
3 
4 Copyright(C) 2005 Eric Lathrop <eric@ericlathrop.com>
5 Copyright(C) 2007 Andrew Smith <http://littlesvr.ca/contact.php>
6 
7 Any code in this file may be redistributed or modified under the terms of
8 the GNU General Public Licence as published by the Free Software
9 Foundation; version 2 of the licence.
10 
11 */
12 
13 #ifdef HAVE_CONFIG_H
14 #  include <config.h>
15 #endif
16 
17 #include <gtk/gtk.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 
23 #include "callbacks.h"
24 #include "interface.h"
25 #include "support.h"
26 #include "main.h"
27 #include "prefs.h"
28 #include "threads.h"
29 #include "util.h"
30 
31 gboolean
for_each_row_deselect(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)32 for_each_row_deselect                  (GtkTreeModel *model,
33                                         GtkTreePath *path,
34                                         GtkTreeIter *iter,
35                                         gpointer data)
36 {
37     gtk_list_store_set(GTK_LIST_STORE(model), iter, COL_RIPTRACK, 0, -1);
38 
39     return FALSE;
40 }
41 
42 gboolean
for_each_row_select(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)43 for_each_row_select                    (GtkTreeModel *model,
44                                         GtkTreePath *path,
45                                         GtkTreeIter *iter,
46                                         gpointer data)
47 {
48     gtk_list_store_set(GTK_LIST_STORE(model), iter, COL_RIPTRACK, 1, -1);
49 
50     return FALSE;
51 }
52 
53 gchar*
format_wavpack_bitrate(GtkScale * scale,gdouble arg1,gpointer user_data)54 format_wavpack_bitrate                 (GtkScale *scale,
55                                         gdouble   arg1,
56                                         gpointer  user_data)
57 {
58     return g_strdup_printf (_("%dKbps"), int_to_wavpack_bitrate((int)arg1));
59 }
60 
61 void
on_about_clicked(GtkToolButton * toolbutton,gpointer user_data)62 on_about_clicked                       (GtkToolButton   *toolbutton,
63                                         gpointer         user_data)
64 {
65     show_aboutbox();
66 }
67 
68 void
on_aboutbox_response(GtkDialog * dialog,gint response_id,gpointer user_data)69 on_aboutbox_response                   (GtkDialog       *dialog,
70                                         gint             response_id,
71                                         gpointer         user_data)
72 {
73     gtk_widget_hide(GTK_WIDGET(dialog));
74     gtk_widget_destroy(GTK_WIDGET(dialog));
75 }
76 
77 void
update_track_offsets(void)78 update_track_offsets (void)
79 {
80     GtkTreeIter iter;
81     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
82     if (store == NULL)
83         return;
84     gboolean rowsleft = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
85     int tracknum;
86 
87     while (rowsleft)
88     {
89         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
90             COL_TRACKNUM, &tracknum,
91             -1);
92         gtk_list_store_set(store, &iter,
93             COL_TRACKNUM_VIS, tracknum + global_prefs->first_track_num_offset,
94             -1);
95         rowsleft = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
96     }
97 }
98 
99 gboolean
on_album_artist_focus_out_event(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)100 on_album_artist_focus_out_event        (GtkWidget       *widget,
101                                         GdkEventFocus   *event,
102                                         gpointer         user_data)
103 {
104     const gchar * ctext = gtk_entry_get_text(GTK_ENTRY(widget));
105     gchar * text = malloc(sizeof(gchar) * (strlen(ctext) + 1));
106     if (text == NULL)
107         fatalError("malloc(sizeof(gchar) * (strlen(ctext) + 1)) failed. Out of memory.");
108     strncpy(text, ctext, strlen(ctext)+1);
109 
110     //trim_chars(text, BADCHARS);        // lnr    //Commented out by mrpl
111     trim_whitespace(text);
112 
113     if(text[0] == '\0')
114         gtk_entry_set_text(GTK_ENTRY(widget), "unknown");
115     else
116         gtk_entry_set_text(GTK_ENTRY(widget), text);
117 
118     free(text);
119     return FALSE;
120 }
121 
122 gboolean
on_year_focus_out_event(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)123 on_year_focus_out_event        (GtkWidget       *widget,
124                                         GdkEventFocus   *event,
125                                         gpointer         user_data)
126 {
127     const gchar * ctext = gtk_entry_get_text(GTK_ENTRY(widget));
128     gchar * text = malloc(5);
129     if (text == NULL)
130         fatalError("malloc(5) failed. Out of memory.");
131     strncpy(text, ctext, 5);
132     text[4] = '\0';
133 
134     if((text[0] != '1' && text[0] != '2') || text[1] < '0' || text[1] > '9' ||
135         text[2] < '0' || text[2] > '9' || text[3] < '0' || text[3] > '9')
136     {
137         sprintf(text, "1900");
138     }
139 
140     gtk_entry_set_text(GTK_ENTRY(widget), text);
141 
142     free(text);
143     return FALSE;
144 }
145 
146 gboolean
on_album_title_focus_out_event(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)147 on_album_title_focus_out_event         (GtkWidget       *widget,
148                                         GdkEventFocus   *event,
149                                         gpointer         user_data)
150 {
151     const gchar * ctext = gtk_entry_get_text(GTK_ENTRY(widget));
152     gchar * text = malloc(sizeof(gchar) * (strlen(ctext) + 1));
153     if (text == NULL)
154         fatalError("malloc(sizeof(gchar) * (strlen(ctext) + 1)) failed. Out of memory.");
155     strncpy(text, ctext, strlen(ctext)+1);
156 
157     //trim_chars(text, BADCHARS);        // lnr    //Commented out by mrpl
158     trim_whitespace(text);
159 
160     if(text[0] == '\0')
161         gtk_entry_set_text(GTK_ENTRY(widget), "unknown");
162     else
163         gtk_entry_set_text(GTK_ENTRY(widget), text);
164 
165     free(text);
166     return FALSE;
167 }
168 
169 // lnr
170 gboolean
on_album_genre_focus_out_event(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)171 on_album_genre_focus_out_event         (GtkWidget       *widget,
172                                         GdkEventFocus   *event,
173                                         gpointer         user_data)
174 {
175     const gchar * ctext = gtk_entry_get_text(GTK_ENTRY(widget));
176 
177     gchar * text = malloc(sizeof(gchar) * (strlen(ctext) + 1));
178 
179     if (text == NULL)
180         fatalError("malloc(sizeof(gchar) * (strlen(ctext) + 1)) failed. Out of memory.");
181 
182     strncpy(text, ctext, strlen(ctext)+1);
183 
184     //trim_chars(text, BADCHARS);        // lnr    //Commented out by mrpl
185     trim_whitespace(text);
186 
187     if(text[0] == '\0')
188         gtk_entry_set_text(GTK_ENTRY(widget), "Unknown");
189     else
190         gtk_entry_set_text(GTK_ENTRY(widget), text);
191 
192     free(text);
193 
194     return FALSE;
195 }
196 
197 // First track number arbitrarily limited to 1001
198 gboolean
on_tracknum_first_focus_out_event(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)199 on_tracknum_first_focus_out_event      (GtkWidget       *widget,
200                                         GdkEventFocus   *event,
201                                         gpointer         user_data)
202 {
203     const gchar * ctext = gtk_entry_get_text(GTK_ENTRY(widget));
204     int first = atoi(ctext);
205 
206     if (first >= 1 && first <= 1001)
207     {
208         global_prefs->first_track_num_offset = first - 1;
209     }
210     update_track_offsets();
211 
212     char txt[16];
213     snprintf(txt, 16, "%d", global_prefs->first_track_num_offset + 1);
214     gtk_entry_set_text(GTK_ENTRY (widget), txt);
215 
216     return FALSE;
217 }
218 
219 void
on_tracknum_width_changed_event(GtkComboBox * combobox,gpointer user_data)220 on_tracknum_width_changed_event        (GtkComboBox     *combobox,
221                                         gpointer         user_data)
222 {
223     gint selected = gtk_combo_box_get_active(combobox);
224     int width = selected + 1;
225 
226     if (width >= 1 && width <= 4)
227     {
228         global_prefs->track_num_width = width;
229     }
230 
231     gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), global_prefs->track_num_width - 1);
232 }
233 
234 void
on_artist_edited(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,gpointer user_data)235 on_artist_edited                    (GtkCellRendererText *cell,
236                                      gchar               *path_string,
237                                      gchar               *new_text,
238                                      gpointer             user_data)
239 {
240     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(
241                     GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
242     GtkTreeIter iter;
243 
244     //trim_chars(new_text, BADCHARS);        // lnr    //Commented out by mrpl
245     trim_whitespace(new_text);
246 
247     gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path_string);
248 
249     if(new_text[0] == '\0')
250         gtk_list_store_set(store, &iter, COL_TRACKARTIST, "unknown", -1);
251     else
252         gtk_list_store_set(store, &iter, COL_TRACKARTIST, new_text, -1);
253 }
254 
255 void
on_cancel_clicked(GtkButton * button,gpointer user_data)256 on_cancel_clicked                      (GtkButton       *button,
257                                         gpointer         user_data)
258 {
259     abort_threads();
260 }
261 
262 gboolean
on_cddb_update_closed(GtkWidget * widget,GdkEvent * event,GtkWidget ** update_window)263 on_cddb_update_closed         (GtkWidget *widget,
264                                GdkEvent  *event,
265                                GtkWidget**      update_window)
266 {
267     *update_window = NULL;
268 
269     return FALSE;
270 }
271 
272 void
on_deselect_all_click(GtkMenuItem * menuitem,gpointer data)273 on_deselect_all_click                  (GtkMenuItem *menuitem,
274                                         gpointer data)
275 {
276     GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist")));
277 
278     gtk_tree_model_foreach(model, for_each_row_deselect, NULL);
279 }
280 
281 void
on_vbr_toggled(GtkToggleButton * togglebutton,gpointer user_data)282 on_vbr_toggled                         (GtkToggleButton *togglebutton,
283                                         gpointer         user_data)
284 {
285     char bitrate[8];
286     GtkRange* range;
287     bool vbr;
288 
289     /* update the displayed vbr, as it's different for vbr and non-vbr */
290     vbr = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton));
291     range = GTK_RANGE(lookup_widget(win_prefs, "mp3bitrate"));
292     snprintf(bitrate, 8, _("%dKbps"), int_to_bitrate((int)gtk_range_get_value(range), vbr));
293     gtk_label_set_text(GTK_LABEL(lookup_widget(win_prefs, "bitrate_lbl_2")), bitrate);
294 }
295 
296 void
on_hybrid_toggled(GtkToggleButton * togglebutton,gpointer user_data)297 on_hybrid_toggled                      (GtkToggleButton *togglebutton,
298                                         gpointer         user_data)
299 {
300     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(win_prefs, "wavpack_hybrid"))))
301     {
302         gtk_widget_set_sensitive(lookup_widget(win_prefs, "wavpack_bitrate_lbl"), TRUE);
303         gtk_widget_set_sensitive(lookup_widget(win_prefs, "wavpack_bitrate_slider"), TRUE);
304     }
305     else
306     {
307         gtk_widget_set_sensitive(lookup_widget(win_prefs, "wavpack_bitrate_lbl"), FALSE);
308         gtk_widget_set_sensitive(lookup_widget(win_prefs, "wavpack_bitrate_slider"), FALSE);
309     }
310 }
311 
312 void
on_mp3bitrate_value_changed(GtkRange * range,gpointer user_data)313 on_mp3bitrate_value_changed            (GtkRange        *range,
314                                         gpointer         user_data)
315 {
316     char bitrate[8];
317     bool vbr;
318 
319     vbr = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(win_prefs, "mp3_vbr")));
320     snprintf(bitrate, 8, _("%dKbps"), int_to_bitrate((int)gtk_range_get_value(range), vbr));
321     gtk_label_set_text(GTK_LABEL(lookup_widget(win_prefs, "bitrate_lbl_2")), bitrate);
322 }
323 
324 void
on_fdkaac_bitrate_value_changed(GtkRange * range,gpointer user_data)325 on_fdkaac_bitrate_value_changed            (GtkRange        *range,
326                                         gpointer         user_data)
327 {
328     char bitrate[8];
329     snprintf(bitrate, 8, _("%dKbps"), int_to_bitrate((int)gtk_range_get_value(range), 0));
330     gtk_label_set_text(GTK_LABEL(lookup_widget(win_prefs, "fdkaac_bitrate_lbl_2")), bitrate);
331 }
332 
333 void
on_opusrate_value_changed(GtkRange * range,gpointer user_data)334 on_opusrate_value_changed           (GtkRange   *range,
335                                      gpointer   user_data)
336 {
337     char bitrate[8];
338     snprintf(bitrate, 8, _("%dKbps"), int_to_bitrate((int)gtk_range_get_value(range), FALSE));
339     gtk_label_set_text(GTK_LABEL(lookup_widget(win_prefs, "bitrate_lbl_4")), bitrate);
340 }
341 
342 void
on_musepackbitrate_value_changed(GtkRange * range,gpointer user_data)343 on_musepackbitrate_value_changed            (GtkRange        *range,
344                                              gpointer         user_data)
345 {
346     char bitrate[8];
347 
348     snprintf(bitrate, 8, _("%dKbps"), int_to_musepack_bitrate((int)gtk_range_get_value(range)));
349     gtk_label_set_text(GTK_LABEL(lookup_widget(win_prefs, "bitrate_lbl_3")), bitrate);
350 }
351 
352 void
on_pick_disc_changed(GtkComboBox * combobox,gpointer user_data)353 on_pick_disc_changed                   (GtkComboBox     *combobox,
354                                         gpointer         user_data)
355 {
356     gint selected = gtk_combo_box_get_active(combobox);
357     if (gbl_disc_matches == NULL ||
358         selected == -1 ||
359         (guint)selected >= g_list_length(gbl_disc_matches))
360         return;
361     cddb_disc_t * disc = g_list_nth_data(gbl_disc_matches, selected);
362     update_tracklist(disc);
363 }
364 
365 void
on_preferences_clicked(GtkToolButton * toolbutton,gpointer user_data)366 on_preferences_clicked                 (GtkToolButton   *toolbutton,
367                                         gpointer         user_data)
368 {
369     win_prefs = create_prefs();
370     gtk_widget_show(win_prefs);
371 }
372 
373 void
on_prefs_response(GtkDialog * dialog,gint response_id,gpointer user_data)374 on_prefs_response                      (GtkDialog       *dialog,
375                                         gint             response_id,
376                                         gpointer         user_data)
377 {
378     //gtk_widget_hide(GTK_WIDGET(dialog));
379 
380     if (response_id == GTK_RESPONSE_OK)
381     {
382         if (!prefs_are_valid())
383             return;
384 
385         get_prefs_from_widgets(global_prefs);
386         save_prefs(global_prefs);
387         toggle_allow_tracknum();
388     }
389 
390     gtk_widget_destroy(GTK_WIDGET(dialog));
391 }
392 
393 void
on_prefs_show(GtkWidget * widget,gpointer user_data)394 on_prefs_show                          (GtkWidget       *widget,
395                                         gpointer         user_data)
396 {
397     set_widgets_from_prefs(global_prefs);
398 }
399 
400 
401 void
on_edit_capitalize_clicked(void)402 on_edit_capitalize_clicked (void)
403 {
404     // "Capitalize Artists & Titles"
405 
406     GtkTreeIter iter;
407     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
408     if (store == NULL)
409         return;
410 
411     gboolean rowsleft = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
412     while (rowsleft)
413     {
414         int riptrack;
415         char * trackartist = NULL;
416         char * tracktitle = NULL;
417 
418         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
419             COL_RIPTRACK, &riptrack,
420             COL_TRACKARTIST, &trackartist,
421             COL_TRACKTITLE, &tracktitle,
422             -1);
423 
424         if (riptrack)       // Only modify selected tracks
425         {
426             int start_of_word = 1;
427             for (char *cp = trackartist; *cp; ++cp)
428             {
429                 if (isspace(*cp))
430                 {
431                     start_of_word = 1;
432                 }
433                 else
434                 if (start_of_word)
435                 {
436                     if (islower(*cp))
437                         *cp = toupper(*cp);
438                     start_of_word = 0;
439                 }
440             }
441 
442             start_of_word = 1;
443             for (char *cp = tracktitle; *cp; ++cp)
444             {
445                 if (isspace(*cp))
446                 {
447                     start_of_word = 1;
448                 }
449                 else
450                 if (start_of_word)
451                 {
452                     if (islower(*cp))
453                         *cp = toupper(*cp);
454                     start_of_word = 0;
455                 }
456             }
457 
458             // Write modified values back into widgets
459             gtk_list_store_set(store, &iter,
460                 COL_TRACKARTIST, trackartist,
461                 COL_TRACKTITLE, tracktitle,
462                 -1);
463         }
464 
465         if (trackartist)
466             free (trackartist);
467         if (tracktitle)
468             free (tracktitle);
469 
470         rowsleft = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
471     }
472 }
473 
474 void
on_edit_split_clicked(void)475 on_edit_split_clicked (void)
476 {
477     // "Split 'Artist/Title' in Title field"
478 
479     GtkTreeIter iter;
480     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
481     if (store == NULL)
482         return;
483 
484     gboolean rowsleft = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
485     while (rowsleft)
486     {
487         int riptrack;
488         char * trackartist = NULL;
489         char * tracktitle = NULL;
490 
491         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
492             COL_RIPTRACK, &riptrack,
493             COL_TRACKTITLE, &tracktitle,
494             -1);
495 
496         if (riptrack)       // Only modify selected tracks
497         {
498             char *separator = strchr(tracktitle, global_prefs->concatenated_track_separator[0]);
499             if (separator != NULL)
500             {
501                 // If separator is present, split Artist/Title
502                 trackartist = strdup(tracktitle);
503                 separator = strchr(trackartist, global_prefs->concatenated_track_separator[0]);
504                 char* cp = separator + 1;
505                 while (*cp != '\0' && isspace(*cp))     // Remove space after separator
506                     cp++;
507                 strcpy(tracktitle, cp);
508                 *separator = '\0';
509                 cp = separator - 1;
510                 while (cp >= trackartist && *cp && isspace(*cp))     // Remove space before separator
511                 {
512                     *cp = '\0';
513                     cp--;
514                 }
515 
516                 // Write modified values back into widgets
517                 gtk_list_store_set(store, &iter,
518                     COL_TRACKARTIST, trackartist,
519                     COL_TRACKTITLE, tracktitle,
520                     -1);
521             }
522         }
523 
524         if (trackartist)
525             free (trackartist);
526         if (tracktitle)
527             free (tracktitle);
528 
529         rowsleft = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
530     }
531 }
532 
533 static void
concatenate_artist_track(void)534 concatenate_artist_track(void)
535 {
536     GtkTreeIter iter;
537     GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(
538                           GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
539 
540     gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
541 
542     while(valid)
543     {
544         GValue gTitle = {0, };
545         gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, COL_TRACKTITLE,
546                 &gTitle);
547         const char* title = g_value_get_string(&gTitle);
548 
549         GValue gArtist = {0, };
550         gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, COL_TRACKARTIST,
551                 &gArtist);
552         const char* artist = g_value_get_string(&gArtist);
553 
554         char separator[] = {"   "};
555         separator[1] = global_prefs->concatenated_track_separator[0];
556 
557         char* artist_and_track = malloc(strlen(artist)
558                                         + strlen(title)
559                                         + strlen(separator) + 1);
560 
561         if(!artist_and_track)
562         {
563             fatalError("malloc(strlen(artist)+strlen(title)+strlen(separator)+1)"
564                        " failed. Out of memory");
565         }
566 
567         artist_and_track[0] = '\0';
568         strcat(artist_and_track, artist);
569         strcat(artist_and_track, separator);
570         strcat(artist_and_track, title);
571 
572         gtk_list_store_set(store, &iter, COL_TRACKARTISTTITLE, artist_and_track, -1);
573 
574         free(artist_and_track);
575 
576         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
577     }
578 }
579 
580 static void
toggle_joined_title_artist(void)581 toggle_joined_title_artist                      (void)
582 {
583     if(join_artist_title <= JOIN_OFF)
584     {
585         concatenate_artist_track();
586         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(win_main,
587                         "single_artist")), TRUE);
588         join_artist_title = JOIN_ON;
589     }
590     else
591     {
592         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(win_main,
593                         "single_artist")), FALSE);
594         join_artist_title = JOIN_OFF;
595     }
596 
597     GtkTreeViewColumn * concatenatedCol = gtk_tree_view_get_column(
598             GTK_TREE_VIEW(tracklist), COL_TRACKARTISTTITLE);
599     gtk_tree_view_column_set_visible(concatenatedCol, join_artist_title == JOIN_ON);
600 
601     GtkTreeViewColumn * titleCol = gtk_tree_view_get_column(GTK_TREE_VIEW(tracklist),
602             COL_TRACKTITLE );
603     gtk_tree_view_column_set_visible(titleCol, join_artist_title == JOIN_OFF);
604 }
605 
606 void
on_edit_swap_clicked(void)607 on_edit_swap_clicked (void)
608 {
609     // "Swap Artist <=> Title"
610 
611     GtkTreeIter iter;
612     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
613     if (store == NULL)
614         return;
615 
616     gboolean rowsleft = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
617     while (rowsleft)
618     {
619         int riptrack;
620         char * trackartist = NULL;
621         char * tracktitle = NULL;
622 
623         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
624             COL_RIPTRACK, &riptrack,
625             COL_TRACKARTIST, &trackartist,
626             COL_TRACKTITLE, &tracktitle,
627             -1);
628 
629         if (riptrack)       // Only modify selected tracks
630         {
631             // Write swapped values back into widgets
632             gtk_list_store_set(store, &iter,
633                 COL_TRACKARTIST, tracktitle,
634                 COL_TRACKTITLE, trackartist,
635                 -1);
636         }
637 
638         if (trackartist)
639             free (trackartist);
640         if (tracktitle)
641             free (tracktitle);
642 
643         rowsleft = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
644     }
645 }
646 
647 void
on_press_f2(void)648 on_press_f2                       (void)
649 {
650     GtkWidget* treeView;
651     GtkTreePath* treePath;
652     GtkTreeViewColumn* focusColumn;
653 
654     treeView = lookup_widget(win_main, "tracklist");
655 
656     if (!GTK_WIDGET_HAS_FOCUS(treeView))
657         return;
658 
659     gtk_tree_view_get_cursor(GTK_TREE_VIEW(treeView), &treePath, &focusColumn);
660 
661     if (treePath == NULL || focusColumn == NULL)
662         return;
663 
664     gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeView), treePath, focusColumn, TRUE);
665 }
666 
667 void
on_lookup_clicked(GtkToolButton * toolbutton,gpointer user_data)668 on_lookup_clicked                     (GtkToolButton   *toolbutton,
669                                        gpointer         user_data)
670 {
671     /* i need to lock myself in refresh()->lookup_disc() */
672     /* another possible solution for this problem:
673     static GThread *main_thread = NULL;
674 
675     void thread_helpers_init (void) {
676        main_thread = g_thread_self ();
677     }
678     gboolean thread_helpers_in_main_thread (void) {
679        return (main_thread == g_thread_self ());
680     }
681     void thread_helpers_lock_gdk (void) {
682        if (!thread_helpers_in_main_thread ())  gdk_threads_enter ();
683     }
684     void thread_helpers_unlock_gdk (void) {
685        if (!thread_helpers_in_main_thread ()) gdk_threads_leave ();
686     }
687     */
688     refresh();
689 }
690 
691 void
on_rip_button_clicked(GtkButton * button,gpointer user_data)692 on_rip_button_clicked                  (GtkButton       *button,
693                                         gpointer         user_data)
694 {
695     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
696     if (store == NULL)
697     {
698         GtkWidget * dialog;
699         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
700                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
701                                         _("No CD is inserted. Please insert a CD into the CD-ROM drive."));
702         gtk_dialog_run(GTK_DIALOG(dialog));
703         gtk_widget_destroy(dialog);
704         return;
705     }
706 
707     dorip();
708 }
709 
710 void
on_rip_mp3_toggled(GtkToggleButton * togglebutton,gpointer user_data)711 on_rip_mp3_toggled                     (GtkToggleButton *togglebutton,
712                                         gpointer         user_data)
713 {
714     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("lame"))
715     {
716         GtkWidget * dialog;
717 
718         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
719                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
720                                         _("%s was not found in your path. Asunder requires it to create %s files. "
721                                         "All %s functionality is disabled."),
722                                         "'lame'", "MP3", "MP3");
723         gtk_dialog_run(GTK_DIALOG(dialog));
724         gtk_widget_destroy(dialog);
725 
726         global_prefs->rip_mp3 = 0;
727         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_mp3);
728     }
729 
730     if (!gtk_toggle_button_get_active(togglebutton))
731         disable_mp3_widgets();
732     else
733         enable_mp3_widgets();
734 }
735 
736 void
on_rip_fdkaac_toggled(GtkToggleButton * togglebutton,gpointer user_data)737 on_rip_fdkaac_toggled                     (GtkToggleButton *togglebutton,
738                                         gpointer         user_data)
739 {
740     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("fdkaac"))
741     {
742         GtkWidget * dialog;
743 
744         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
745                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
746                                         _("%s was not found in your path. Asunder requires it to create %s files. "
747                                         "All %s functionality is disabled."),
748                                         "'fdkaac'", "AAC", "AAC");
749         gtk_dialog_run(GTK_DIALOG(dialog));
750         gtk_widget_destroy(dialog);
751 
752         global_prefs->rip_fdkaac = 0;
753         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_fdkaac);
754     }
755 
756     if (!gtk_toggle_button_get_active(togglebutton))
757         disable_fdkaac_widgets();
758     else
759         enable_fdkaac_widgets();
760 }
761 
762 void
on_rip_flac_toggled(GtkToggleButton * togglebutton,gpointer user_data)763 on_rip_flac_toggled                    (GtkToggleButton *togglebutton,
764                                         gpointer         user_data)
765 {
766     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("flac"))
767     {
768         GtkWidget * dialog;
769         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
770                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
771                                         _("%s was not found in your path. Asunder requires it to create %s files. "
772                                         "All %s functionality is disabled."),
773                                         "'flac'", "FLAC", "FLAC");
774         gtk_dialog_run(GTK_DIALOG(dialog));
775         gtk_widget_destroy(dialog);
776 
777         global_prefs->rip_flac = 0;
778         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_flac);
779     }
780 
781     if (!gtk_toggle_button_get_active(togglebutton))
782         disable_flac_widgets();
783     else
784         enable_flac_widgets();
785 }
786 
787 void
on_rip_ogg_toggled(GtkToggleButton * togglebutton,gpointer user_data)788 on_rip_ogg_toggled                     (GtkToggleButton *togglebutton,
789                                         gpointer         user_data)
790 {
791     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("oggenc"))
792     {
793         GtkWidget * dialog;
794         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
795                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
796                                         _("%s was not found in your path. Asunder requires it to create %s files. "
797                                         "All %s functionality is disabled."),
798                                         "'oggenc'", "OGG", "OGG");
799         gtk_dialog_run(GTK_DIALOG(dialog));
800         gtk_widget_destroy(dialog);
801 
802         global_prefs->rip_ogg = 0;
803         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_ogg);
804     }
805 
806     if (!gtk_toggle_button_get_active(togglebutton))
807         disable_ogg_widgets();
808     else
809         enable_ogg_widgets();
810 }
811 
812 void
on_rip_opus_toggled(GtkToggleButton * togglebutton,gpointer user_data)813 on_rip_opus_toggled                     (GtkToggleButton *togglebutton,
814                                         gpointer user_data)
815 {
816     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("opusenc"))
817     {
818         GtkWidget *dialog;
819         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
820                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
821                                         _("%s was not found in your path. Asunder requires it to create %s files. "
822                                           "All %s functionality is disabled."),
823                                         "'opusenc'", "OPUS", "opus");
824         gtk_dialog_run (GTK_DIALOG(dialog));
825         gtk_widget_destroy(dialog);
826 
827         global_prefs->rip_opus=0;
828         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_opus);
829 
830     }
831 
832     if (!gtk_toggle_button_get_active(togglebutton))
833         disable_opus_widgets();
834     else
835         enable_opus_widgets();
836 }
837 
838 void
on_rip_wavpack_toggled(GtkToggleButton * togglebutton,gpointer user_data)839 on_rip_wavpack_toggled                 (GtkToggleButton *togglebutton,
840                                         gpointer         user_data)
841 {
842     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("wavpack"))
843     {
844         GtkWidget * dialog;
845         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
846                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
847                                         _("%s was not found in your path. Asunder requires it to create %s files. "
848                                         "All %s functionality is disabled."),
849                                         "'wavpack'", "WV", "wavpack");
850         gtk_dialog_run(GTK_DIALOG(dialog));
851         gtk_widget_destroy(dialog);
852 
853         global_prefs->rip_wavpack = 0;
854         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_wavpack);
855     }
856 
857     if (!gtk_toggle_button_get_active(togglebutton))
858         disable_wavpack_widgets();
859     else
860         enable_wavpack_widgets();
861 }
862 
863 void
on_rip_monkey_toggled(GtkToggleButton * togglebutton,gpointer user_data)864 on_rip_monkey_toggled                  (GtkToggleButton *togglebutton,
865                                         gpointer         user_data)
866 {
867     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("mac"))
868     {
869         GtkWidget * dialog;
870         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
871                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
872                                         _("%s was not found in your path. Asunder requires it to create %s files. "
873                                         "All %s functionality is disabled."),
874                                         "'mac'", "APE", "Monkey's Audio");
875         gtk_dialog_run(GTK_DIALOG(dialog));
876         gtk_widget_destroy(dialog);
877 
878         global_prefs->rip_monkey = 0;
879         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_monkey);
880     }
881 
882     if (!gtk_toggle_button_get_active(togglebutton))
883         disable_monkey_widgets();
884     else
885         enable_monkey_widgets();
886 }
887 
888 void
on_rip_musepack_toggled(GtkToggleButton * togglebutton,gpointer user_data)889 on_rip_musepack_toggled                  (GtkToggleButton *togglebutton,
890                                           gpointer         user_data)
891 {
892     if (gtk_toggle_button_get_active(togglebutton) && !program_exists("mpcenc"))
893     {
894         GtkWidget * dialog;
895         dialog = gtk_message_dialog_new(GTK_WINDOW(win_main),
896                                         GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
897                                         _("%s was not found in your path. Asunder requires it to create %s files. "
898                                         "All %s functionality is disabled."),
899                                         "'mpcenc'", "MPC", "Musepack");
900         gtk_dialog_run(GTK_DIALOG(dialog));
901         gtk_widget_destroy(dialog);
902 
903         global_prefs->rip_musepack = 0;
904         gtk_toggle_button_set_active(togglebutton, global_prefs->rip_musepack);
905     }
906 
907     if (!gtk_toggle_button_get_active(togglebutton))
908         disable_musepack_widgets();
909     else
910         enable_musepack_widgets();
911 }
912 
913 void
on_rip_toggled(GtkCellRendererToggle * cell,gchar * path_string,gpointer user_data)914 on_rip_toggled                       (GtkCellRendererToggle *cell,
915                                       gchar                 *path_string,
916                                       gpointer               user_data)
917 {
918     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
919     GtkTreeIter iter;
920     int toggled;
921 
922     gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path_string);
923     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
924             COL_RIPTRACK, &toggled,
925             -1);
926     gtk_list_store_set(store, &iter,
927             COL_RIPTRACK, !toggled,
928             -1);
929 }
930 
931 void
on_select_all_click(GtkMenuItem * menuitem,gpointer data)932 on_select_all_click                    (GtkMenuItem *menuitem,
933                                         gpointer data)
934 {
935     GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(lookup_widget(win_main, "tracklist")));
936 
937     gtk_tree_model_foreach(model, for_each_row_select, NULL);
938 }
939 
940 void
on_single_artist_toggled(GtkToggleButton * togglebutton,gpointer user_data)941 on_single_artist_toggled               (GtkToggleButton *togglebutton,
942                                         gpointer         user_data)
943 {
944     GtkTreeViewColumn * artistCol = gtk_tree_view_get_column(GTK_TREE_VIEW(tracklist),
945             COL_TRACKARTIST ); //lnr
946     gtk_tree_view_column_set_visible(artistCol,
947             !gtk_toggle_button_get_active(togglebutton));
948 }
949 
950 // lnr
951 void
on_title_edited(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,gpointer user_data)952 on_title_edited                    (GtkCellRendererText *cell,
953                                     gchar               *path_string,
954                                     gchar               *new_text,
955                                     gpointer             user_data)
956 {
957     GtkListStore * store = GTK_LIST_STORE(gtk_tree_view_get_model(
958                     GTK_TREE_VIEW(lookup_widget(win_main, "tracklist"))));
959     GtkTreeIter iter;
960 
961     //trim_chars(new_text, BADCHARS);        // lnr    //Commented out by mrpl
962     trim_whitespace(new_text);
963 
964     gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path_string);
965 
966     if(new_text[0] == '\0')
967         gtk_list_store_set(store, &iter, COL_TRACKTITLE, "unknown", -1);
968     else
969         gtk_list_store_set(store, &iter, COL_TRACKTITLE, new_text, -1);
970 
971 
972 }
973 
974 gboolean
on_tracklist_mouse_click(GtkWidget * treeView,GdkEventButton * event,gpointer user_data)975 on_tracklist_mouse_click               (GtkWidget* treeView,
976                                         GdkEventButton* event,
977                                         gpointer user_data)
978 {
979     if( event->type == GDK_BUTTON_PRESS && event->button == 3 &&
980         GTK_WIDGET_SENSITIVE(lookup_widget(win_main, "rip_button")) )
981     {
982         GtkWidget* menu;
983         GtkWidget* menuItem;
984 
985         menu = gtk_menu_new();
986 
987         menuItem = gtk_menu_item_new_with_label(_("Select all for ripping"));
988         g_signal_connect(menuItem, "activate",
989                          G_CALLBACK(on_select_all_click), NULL);
990         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
991         gtk_widget_show_all(menu);
992 
993         menuItem = gtk_menu_item_new_with_label(_("Deselect all for ripping"));
994         g_signal_connect(menuItem, "activate",
995                          G_CALLBACK(on_deselect_all_click), NULL);
996         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
997         gtk_widget_show_all(menu);
998 
999         menuItem = gtk_menu_item_new_with_label(_("Capitalize Artists & Titles"));
1000         g_signal_connect(menuItem, "activate",
1001                          G_CALLBACK(on_edit_capitalize_clicked), NULL);
1002         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
1003         gtk_widget_show_all(menu);
1004 
1005         const char splitTemplate[] = "Split 'Artist%cTitle' in Title field";
1006         char splitLabel[sizeof(splitTemplate)];
1007         sprintf(splitLabel, splitTemplate, global_prefs->concatenated_track_separator[0]);
1008 
1009         if(join_artist_title == JOIN_UNSET)
1010         {
1011             menuItem = gtk_menu_item_new_with_label(_(splitLabel));
1012             g_signal_connect(menuItem, "activate",
1013                              G_CALLBACK(on_edit_split_clicked), NULL);
1014             gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
1015             gtk_widget_show_all(menu);
1016         }
1017 
1018         const char joinTemplate[] = "Join 'Artist%cTitle' in Title field";
1019         char joinLabel[sizeof(joinTemplate)];
1020         sprintf(joinLabel, joinTemplate, global_prefs->concatenated_track_separator[0]);
1021 
1022         menuItem = gtk_menu_item_new_with_label(_(
1023             join_artist_title == JOIN_ON ? splitLabel : joinLabel));
1024         g_signal_connect(menuItem, "activate",
1025                          G_CALLBACK(toggle_joined_title_artist), NULL);
1026         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
1027         gtk_widget_show_all(menu);
1028 
1029         menuItem = gtk_menu_item_new_with_label(_("Swap Artist <=> Title"));
1030         g_signal_connect(menuItem, "activate",
1031                          G_CALLBACK(on_edit_swap_clicked), NULL);
1032         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
1033         gtk_widget_show_all(menu);
1034 
1035         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1036                        event->button, gdk_event_get_time((GdkEvent*)event));
1037 
1038         /* no need for signal to propagate */
1039         return TRUE;
1040     }
1041 
1042     return FALSE;
1043 }
1044 
1045 void
on_rip_header_click(GtkTreeViewColumn * treeviewcolumn,gpointer user_data)1046 on_rip_header_click                 (GtkTreeViewColumn *treeviewcolumn,
1047                                      gpointer           user_data)
1048 {
1049     // Select/Deselect all tracks.
1050     // Get first checkbox and set all to opposite value.
1051     GtkTreeIter iter;
1052     int riptrack;
1053     GtkTreeModel * model = gtk_tree_view_get_model(
1054         GTK_TREE_VIEW(lookup_widget(win_main, "tracklist")));
1055 
1056     if (!gtk_tree_model_get_iter_first(model, &iter))
1057         return;
1058     gtk_tree_model_get(model, &iter, COL_RIPTRACK, &riptrack, -1);
1059 
1060     if (riptrack)
1061         gtk_tree_model_foreach(model, for_each_row_deselect, NULL);
1062     else
1063         gtk_tree_model_foreach(model, for_each_row_select, NULL);
1064 }
1065 
1066 void
on_window_close(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)1067 on_window_close                        (GtkWidget       *widget,
1068                                         GdkEventFocus   *event,
1069                                         gpointer         user_data)
1070 {
1071     gtk_window_get_size(GTK_WINDOW(win_main),
1072             &global_prefs->main_window_width,
1073             &global_prefs->main_window_height);
1074 
1075     save_prefs(global_prefs);
1076 
1077     gtk_main_quit();
1078 }
1079