1 /* -*- linux-c -*-
2 Copyright (C) 2004 Tom Szilagyi
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 $Id: playlist.c 1293 2014-05-04 22:06:57Z tszilagyi $
19 */
20
21 #include <config.h>
22
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <dirent.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <glib.h>
31 #include <glib/gstdio.h>
32 #include <glib-object.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <gdk-pixbuf/gdk-pixbuf.h>
35 #include <gtk/gtk.h>
36 #include <libxml/globals.h>
37 #include <libxml/parser.h>
38 #include <libxml/tree.h>
39
40 #ifdef HAVE_IFP
41 #include "ifp_device.h"
42 #endif /* HAVE_IFP */
43
44 #ifdef HAVE_TRANSCODING
45 #include "export.h"
46 #endif /* HAVE_TRANSCODING */
47
48 #include "athread.h"
49 #include "ext_lua.h"
50 #include "common.h"
51 #include "utils.h"
52 #include "utils_gui.h"
53 #include "core.h"
54 #include "httpc.h"
55 #include "rb.h"
56 #include "gui_main.h"
57 #include "music_browser.h"
58 #include "file_info.h"
59 #include "decoder/file_decoder.h"
60 #include "metadata_api.h"
61 #include "volume.h"
62 #include "options.h"
63 #include "i18n.h"
64 #include "search_playlist.h"
65 #include "playlist.h"
66
67
68 extern options_t options;
69
70 extern char pl_color_active[14];
71 extern char pl_color_inactive[14];
72
73 extern PangoFontDescription *fd_playlist;
74 extern PangoFontDescription *fd_statusbar;
75
76 extern GtkWidget * main_window;
77
78 extern GtkTreeSelection * music_select;
79
80 extern int is_file_loaded;
81 extern int is_paused;
82 extern int allow_seeks;
83
84 extern rb_t * rb_gui2disk;
85
86 extern GtkWidget * playlist_toggle;
87
88
89 GtkWidget * playlist_window;
90 GtkWidget * playlist_color_indicator;
91
92 gint playlist_scroll_up_tag = -1;
93 gint playlist_scroll_dn_tag = -1;
94
95 GtkWidget * statusbar_total;
96 GtkWidget * statusbar_selected;
97 GtkWidget * statusbar_total_label;
98 GtkWidget * statusbar_selected_label;
99
100
101 /* popup menus */
102 GtkWidget * add_menu;
103 GtkWidget * add__files;
104 GtkWidget * add__dir;
105 GtkWidget * add__url;
106 GtkWidget * sel_menu;
107 GtkWidget * sel__none;
108 GtkWidget * sel__all;
109 GtkWidget * sel__inv;
110 GtkWidget * rem_menu;
111 GtkWidget * cut__sel;
112 GtkWidget * rem__all;
113 GtkWidget * rem__dead;
114 GtkWidget * rem__sel;
115 GtkWidget * plist_menu;
116 GtkWidget * plist__tab_new;
117 GtkWidget * plist__save;
118 GtkWidget * plist__save_all;
119 GtkWidget * plist__load;
120 GtkWidget * plist__enqueue;
121 GtkWidget * plist__load_tab;
122 GtkWidget * plist__rva;
123 GtkWidget * plist__rva_menu;
124 GtkWidget * plist__rva_separate;
125 GtkWidget * plist__rva_average;
126 GtkWidget * plist__reread_file_meta;
127 GtkWidget * plist__fileinfo;
128 GtkWidget * plist__search;
129 GtkWidget * plist__roll;
130 #ifdef HAVE_IFP
131 GtkWidget * plist__send_songs_to_iriver;
132 #endif /* HAVE_IFP */
133 #ifdef HAVE_TRANSCODING
134 GtkWidget * plist__export;
135 #endif /* HAVE_TRANSCODING */
136
137
138 GtkTreeIter * fileinfo_iter = NULL;
139
140 int playlist_dirty;
141 int playlist_auto_save_tag = -1;
142
143 typedef struct {
144 GtkTreeStore * store;
145 int has_album_node;
146 } clipboard_t;
147
148 clipboard_t * clipboard;
149
150 enum {
151 PLAYLIST_INVALID,
152 PLAYLIST_XML_SINGLE,
153 PLAYLIST_XML_MULTI,
154 PLAYLIST_M3U,
155 PLAYLIST_PLS
156 };
157
158 void create_playlist_gui(playlist_t * pl);
159
160 void playlist_notebook_prev_page(void);
161 void playlist_notebook_next_page(void);
162 void playlist_tab_close(GtkButton * button, gpointer data);
163 void playlist_tab_close_undo(void);
164 void playlist_progress_bar_hide(playlist_t * pl);
165
166 playlist_transfer_t * playlist_transfer_get(int mode, char * tab_name, int start_playback);
167
168
169 playlist_data_t * playlist_filemeta_get(char * filename);
170
171 void playlist_unlink_files(playlist_t * pl);
172 void set_cursor_in_playlist(playlist_t * pl, GtkTreeIter *iter, gboolean scroll);
173 void select_active_position_in_playlist(playlist_t * pl);
174
175 void playlist_selection_changed(playlist_t * pl);
176 void playlist_selection_changed_cb(GtkTreeSelection * select, gpointer data);
177
178 void sel__all_cb(gpointer data);
179 void sel__none_cb(gpointer data);
180 void rem__all_cb(gpointer data);
181 void rem__sel_cb(gpointer data);
182 void cut__sel_cb(gpointer data);
183 void plist__search_cb(gpointer data);
184 void plist__roll_cb(gpointer data);
185 void rem_all(playlist_t * pl);
186 void add_files(GtkWidget * widget, gpointer data);
187
188 void tab__rename_cb(gpointer data);
189
190 void add_directory(GtkWidget * widget, gpointer data);
191 void init_plist_menu(GtkWidget *append_menu);
192 void show_active_position_in_playlist(playlist_t * pl);
193 void show_active_position_in_playlist_toggle(playlist_t * pl);
194 void expand_collapse_album_node(playlist_t * pl);
195
196 void playlist_save(playlist_t * pl, char * filename);
197
198 int playlist_get_type(char * filename);
199 void playlist_load_xml_multi(char * filename, int start_playback);
200 void playlist_load_xml_single(char * filename, playlist_transfer_t * pt);
201 void * playlist_load_m3u_thread(void * arg);
202 void * playlist_load_pls_thread(void * arg);
203 void roll_to_active_track(playlist_t * pl, GtkTreeIter *piter);
204
205
206 GtkWidget * playlist_notebook;
207
208 GList * playlists;
209 GList * playlists_closed;
210
211
212 playlist_data_t *
playlist_data_new()213 playlist_data_new() {
214
215 playlist_data_t * data;
216
217 if ((data = (playlist_data_t *)calloc(1, sizeof(playlist_data_t))) == NULL) {
218 fprintf(stderr, "playlist_data_new(): calloc error\n");
219 return NULL;
220 }
221
222 data->voladj = 0.0f;
223 data->duration = 0.0f;
224 data->size = 0;
225 data->flags = 0;
226 data->artist = NULL;
227 data->album = NULL;
228 data->title = NULL;
229 data->display = NULL;
230 data->file = NULL;
231
232 return data;
233 }
234
235 void
playlist_data_copy_noalloc(playlist_data_t * dest,playlist_data_t * src)236 playlist_data_copy_noalloc(playlist_data_t * dest, playlist_data_t * src) {
237
238 if (src->artist) {
239 free_strdup(&dest->artist, src->artist);
240 }
241 if (src->album) {
242 free_strdup(&dest->album, src->album);
243 }
244 if (src->title) {
245 free_strdup(&dest->title, src->title);
246 }
247 if (src->display) {
248 free_strdup(&dest->display, src->display);
249 }
250 if (src->file) {
251 free_strdup(&dest->file, src->file);
252 }
253
254 dest->voladj = src->voladj;
255 dest->duration = src->duration;
256 dest->size = src->size;
257
258 dest->ntracks = src->ntracks;
259 dest->actrack = src->actrack;
260 dest->flags = src->flags;
261 }
262
263 playlist_data_t *
playlist_data_copy(playlist_data_t * src)264 playlist_data_copy(playlist_data_t * src) {
265
266 playlist_data_t * dest;
267
268 if ((dest = playlist_data_new()) == NULL) {
269 return NULL;
270 }
271
272 playlist_data_copy_noalloc(dest, src);
273
274 return dest;
275 }
276
277 void
playlist_data_free(playlist_data_t * data)278 playlist_data_free(playlist_data_t * data) {
279
280 if (data->artist) {
281 free(data->artist);
282 data->artist = NULL;
283 }
284 if (data->album) {
285 free(data->album);
286 data->album = NULL;
287 }
288 if (data->title) {
289 free(data->title);
290 data->title = NULL;
291 }
292 if (data->display) {
293 free(data->display);
294 data->display = NULL;
295 }
296 if (data->file) {
297 free(data->file);
298 data->file = NULL;
299 }
300 free(data);
301 }
302
303 gboolean
playlist_remove_track(GtkTreeStore * store,GtkTreeIter * iter)304 playlist_remove_track(GtkTreeStore * store, GtkTreeIter * iter) {
305
306 playlist_data_t * data;
307
308 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, PL_COL_DATA, &data, -1);
309 playlist_data_free(data);
310
311 return gtk_tree_store_remove(store, iter);
312 }
313
314 void
playlist_clear(GtkTreeStore * store)315 playlist_clear(GtkTreeStore * store) {
316
317 GtkTreeIter iter;
318 GtkTreeIter iter_child;
319 playlist_data_t * data;
320 int i, j;
321
322 i = 0;
323 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, i++)) {
324 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, PL_COL_DATA, &data, -1);
325 playlist_data_free(data);
326
327 j = 0;
328 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter_child, &iter, j++)) {
329 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter_child, PL_COL_DATA, &data, -1);
330 playlist_data_free(data);
331 }
332 }
333
334 gtk_tree_store_clear(store);
335 }
336
337 GtkTreeStore *
playlist_store_new()338 playlist_store_new() {
339
340 return gtk_tree_store_new(PL_COL_COUNT,
341 G_TYPE_STRING, /* title string */
342 G_TYPE_STRING, /* volume adj. displayed */
343 G_TYPE_STRING, /* duration displayed */
344 G_TYPE_STRING, /* color */
345 G_TYPE_INT, /* font weight */
346 G_TYPE_POINTER); /* pointer to struct playlist_data_t */
347 }
348
349 playlist_t *
playlist_new(char * name)350 playlist_new(char * name) {
351
352 playlist_t * pl = NULL;
353
354 if ((pl = (playlist_t *)calloc(1, sizeof(playlist_t))) == NULL) {
355 fprintf(stderr, "playlist_new(): calloc error\n");
356 return NULL;
357 }
358
359 playlists = g_list_append(playlists, pl);
360
361 AQUALUNG_COND_INIT(pl->thread_wait);
362
363 #ifndef HAVE_LIBPTHREAD
364 pl->thread_mutex = g_mutex_new();
365 pl->wait_mutex = g_mutex_new();
366 pl->thread_wait = g_cond_new();
367 #endif /* !HAVE_LIBPTHREAD */
368
369 if (name != NULL) {
370 strncpy(pl->name, name, MAXLEN-1);
371 pl->name_set = 1;
372 } else {
373 strncpy(pl->name, _("(Untitled)"), MAXLEN-1);
374 }
375
376 pl->index = -1;
377
378 pl->store = playlist_store_new();
379
380 return pl;
381 }
382
383 void
playlist_free(playlist_t * pl)384 playlist_free(playlist_t * pl) {
385
386 playlists = g_list_remove(playlists, pl);
387
388 #ifndef HAVE_LIBPTHREAD
389 g_mutex_free(pl->thread_mutex);
390 g_mutex_free(pl->wait_mutex);
391 g_cond_free(pl->thread_wait);
392 #endif /* !HAVE_LIBPTHREAD */
393
394 free(pl);
395 }
396
397
398 gint
playlist_compare_widget(gconstpointer list,gconstpointer widget)399 playlist_compare_widget(gconstpointer list, gconstpointer widget) {
400
401 return ((playlist_t *)list)->widget != widget;
402 }
403
404 gint
playlist_compare_playing(gconstpointer list,gconstpointer dummy)405 playlist_compare_playing(gconstpointer list, gconstpointer dummy) {
406
407 return ((playlist_t *)list)->playing == 0;
408 }
409
410 gint
playlist_compare_name(gconstpointer list,gconstpointer name)411 playlist_compare_name(gconstpointer list, gconstpointer name) {
412
413 return strcmp(((playlist_t *)list)->name, (char *)name);
414 }
415
416 gint
playlist_compare_index(gconstpointer p1,gconstpointer p2)417 playlist_compare_index(gconstpointer p1, gconstpointer p2) {
418
419 int i1 = ((playlist_t *)p1)->index;
420 int i2 = ((playlist_t *)p2)->index;
421
422 if (i1 == -1) {
423 i1 = 1000;
424 }
425
426 if (i2 == -1) {
427 i2 = 1000;
428 }
429
430 if (i1 < i2) {
431 return -1;
432 } else if (i1 > i2) {
433 return 1;
434 }
435
436 return 0;
437 }
438
439 playlist_t *
playlist_find(gconstpointer data,GCompareFunc func)440 playlist_find(gconstpointer data, GCompareFunc func) {
441
442 GList * find = g_list_find_custom(playlists, data, func);
443
444 if (find != NULL) {
445 return (playlist_t *)(find->data);
446 }
447
448 return NULL;
449 }
450
451
452 playlist_t *
playlist_get_current()453 playlist_get_current() {
454
455 int idx = gtk_notebook_get_current_page(GTK_NOTEBOOK(playlist_notebook));
456 GtkWidget * widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(playlist_notebook), idx);
457
458 return playlist_find(widget, playlist_compare_widget);
459 }
460
461 playlist_t *
playlist_get_playing()462 playlist_get_playing() {
463
464 return playlist_find(NULL/*dummy*/, playlist_compare_playing);
465 }
466
467 playlist_t *
playlist_get_by_page_num(int num)468 playlist_get_by_page_num(int num) {
469
470 GtkWidget * widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(playlist_notebook), num);
471
472 playlist_t * pl = playlist_find(widget, playlist_compare_widget);
473
474 if (pl == NULL) {
475 fprintf(stderr, "WARNING: playlist_get_by_page_num() == NULL\n");
476 }
477
478 return pl;
479 }
480
481
482 playlist_t *
playlist_get_by_name(char * name)483 playlist_get_by_name(char * name) {
484
485 return playlist_find(name, playlist_compare_name);
486 }
487
488
489 void
playlist_set_current(playlist_t * pl)490 playlist_set_current(playlist_t * pl) {
491
492 int n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook));
493 int i;
494
495 for (i = 0; i < n; i++) {
496 if (pl->widget == gtk_notebook_get_nth_page(GTK_NOTEBOOK(playlist_notebook), i)) {
497 gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), i);
498 return;
499 }
500 }
501 }
502
503 int
playlist_is_empty(playlist_t * pl)504 playlist_is_empty(playlist_t * pl) {
505
506 GtkTreeIter dummy;
507
508 if (!pl->name_set &&
509 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pl->store), &dummy) == FALSE) {
510 return 1;
511 }
512
513 return 0;
514 }
515
516 void
playlist_set_active(GtkTreeStore * store,GtkTreeIter * piter)517 playlist_set_active(GtkTreeStore * store, GtkTreeIter * piter) {
518
519 playlist_data_t * data;
520
521 gtk_tree_model_get(GTK_TREE_MODEL(store), piter, PL_COL_DATA, &data, -1);
522 PL_SET_FLAG(data, PL_FLAG_ACTIVE);
523
524 gtk_tree_store_set(store, piter, PL_COL_COLO, pl_color_active, -1);
525 if (options.show_active_track_name_in_bold) {
526 gtk_tree_store_set(store, piter, PL_COL_FONT, PANGO_WEIGHT_BOLD, -1);
527 }
528 }
529
530 void
playlist_set_inactive(GtkTreeStore * store,GtkTreeIter * piter)531 playlist_set_inactive(GtkTreeStore * store, GtkTreeIter * piter) {
532
533 playlist_data_t * data;
534
535 gtk_tree_model_get(GTK_TREE_MODEL(store), piter, PL_COL_DATA, &data, -1);
536 PL_UNSET_FLAG(data, PL_FLAG_ACTIVE);
537
538 gtk_tree_store_set(store, piter,
539 PL_COL_COLO, pl_color_inactive,
540 PL_COL_FONT, PANGO_WEIGHT_NORMAL, -1);
541 }
542
543 void
playlist_node_copy(GtkTreeStore * sstore,GtkTreeIter * siter,GtkTreeStore * tstore,GtkTreeIter * titer,GtkTreeIter * iter,int mode)544 playlist_node_copy(GtkTreeStore * sstore, GtkTreeIter * siter,
545 GtkTreeStore * tstore, GtkTreeIter * titer,
546 GtkTreeIter * iter, int mode) {
547
548 gchar * name;
549 gchar * vadj;
550 gchar * dura;
551
552 playlist_data_t * sdata;
553 playlist_data_t * tdata;
554
555 gtk_tree_model_get(GTK_TREE_MODEL(sstore), siter,
556 PL_COL_NAME, &name,
557 PL_COL_VADJ, &vadj,
558 PL_COL_DURA, &dura,
559 PL_COL_DATA, &sdata, -1);
560
561 if ((tdata = playlist_data_copy(sdata)) == NULL) {
562 return;
563 }
564
565 if (mode == 0) {
566 gtk_tree_store_insert_after(tstore, iter, NULL, titer);
567 } else if (mode == 1) {
568 gtk_tree_store_insert_before(tstore, iter, NULL, titer);
569 } else {
570 gtk_tree_store_append(tstore, iter, titer);
571 }
572
573 if (tdata->file) {
574 GtkTreeIter parent;
575 int name_set = 0;
576 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(tstore), &parent, iter)) {
577 playlist_data_t * pdata;
578 gtk_tree_model_get(GTK_TREE_MODEL(tstore), &parent, PL_COL_DATA, &pdata, -1);
579 if (pdata->artist && tdata->artist && pdata->album && tdata->album && tdata->title &&
580 !strcmp(pdata->artist, tdata->artist) && !strcmp(pdata->album, tdata->album)) {
581 gtk_tree_store_set(tstore, iter, PL_COL_NAME, tdata->title, -1);
582 name_set = 1;
583 }
584 }
585 if (!name_set) {
586 char list_str[MAXLEN];
587 playlist_data_get_display_name(list_str, tdata);
588 gtk_tree_store_set(tstore, iter, PL_COL_NAME, list_str, -1);
589 }
590 } else {
591 gtk_tree_store_set(tstore, iter, PL_COL_NAME, name, -1);
592 }
593
594 gtk_tree_store_set(tstore, iter,
595 PL_COL_VADJ, vadj,
596 PL_COL_DURA, dura,
597 PL_COL_COLO, IS_PL_ACTIVE(tdata) ? pl_color_active : pl_color_inactive,
598 PL_COL_FONT, (IS_PL_ACTIVE(tdata) && options.show_active_track_name_in_bold) ?
599 PANGO_WEIGHT_BOLD :
600 PANGO_WEIGHT_NORMAL,
601 PL_COL_DATA, tdata, -1);
602
603 if (sstore != tstore && IS_PL_ACTIVE(sdata)) {
604
605 playlist_set_inactive(tstore, iter);
606
607 if (IS_PL_ALBUM_NODE(tdata)) {
608 char name[MAXLEN];
609 snprintf(name, MAXLEN-1, "%s: %s", tdata->artist, tdata->album);
610 gtk_tree_store_set(tstore, iter, PL_COL_NAME, name, -1);
611 }
612 }
613
614 g_free(name);
615 g_free(vadj);
616 g_free(dura);
617 }
618
619 void
playlist_node_deep_copy(GtkTreeStore * sstore,GtkTreeIter * siter,GtkTreeStore * tstore,GtkTreeIter * titer,int mode)620 playlist_node_deep_copy(GtkTreeStore * sstore, GtkTreeIter * siter,
621 GtkTreeStore * tstore, GtkTreeIter * titer,
622 int mode) {
623
624 GtkTreeIter iter;
625 GtkTreeIter dummy;
626 GtkTreeIter child_siter;
627 int i;
628
629
630 playlist_node_copy(sstore, siter, tstore, titer, &iter, mode);
631
632 i = 0;
633 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(sstore), &child_siter, siter, i++)) {
634 playlist_node_copy(sstore, &child_siter, tstore, &iter, &dummy, 2/*append*/);
635 }
636 }
637
638 int
all_tracks_selected(playlist_t * pl,GtkTreeIter * piter)639 all_tracks_selected(playlist_t * pl, GtkTreeIter * piter) {
640
641 gint j = 0;
642 GtkTreeIter iter_child;
643
644 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_child, piter, j++)) {
645 if (!gtk_tree_selection_iter_is_selected(pl->select, &iter_child)) {
646 return 0;
647 }
648 }
649 return 1;
650 }
651
652 void
playlist_copy(playlist_t * pl)653 playlist_copy(playlist_t * pl) {
654
655 GtkTreeIter iter;
656 gint i = 0;
657
658 if (clipboard == NULL) {
659 if ((clipboard = (clipboard_t *)calloc(1, sizeof(clipboard_t))) == NULL) {
660 fprintf(stderr, "playlist_copy(): calloc error\n");
661 return;
662 }
663 clipboard->store = playlist_store_new();
664 } else {
665 playlist_clear(clipboard->store);
666 }
667
668 clipboard->has_album_node = 0;
669
670 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
671
672 if (gtk_tree_selection_iter_is_selected(pl->select, &iter)) {
673 playlist_node_deep_copy(pl->store, &iter, clipboard->store, NULL, 2);
674 clipboard->has_album_node = 1;
675 continue;
676 }
677
678 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter) == 0) {
679 continue;
680 }
681
682 if (all_tracks_selected(pl, &iter)) {
683 playlist_node_deep_copy(pl->store, &iter, clipboard->store, NULL, 2);
684 clipboard->has_album_node = 1;
685 } else {
686 int j = 0;
687 GtkTreeIter iter_child;
688 GtkTreeIter dummy;
689
690 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_child, &iter, j++)) {
691 if (gtk_tree_selection_iter_is_selected(pl->select, &iter_child)) {
692 playlist_node_copy(pl->store, &iter_child,
693 clipboard->store, NULL, &dummy, 2);
694 }
695 }
696 }
697 }
698 }
699
700 void
playlist_cut(playlist_t * pl)701 playlist_cut(playlist_t * pl) {
702
703 playlist_copy(pl);
704 rem__sel_cb(pl);
705 }
706
707 void
playlist_paste(playlist_t * pl,int before)708 playlist_paste(playlist_t * pl, int before) {
709
710 gint i = 0;
711 GtkTreeIter iter;
712 GtkTreeIter titer;
713 GtkTreePath * path;
714
715 if (clipboard == NULL) {
716 return;
717 }
718
719 gtk_tree_view_get_cursor(GTK_TREE_VIEW(pl->view), &path, NULL);
720
721 if (path != NULL) {
722
723 if (clipboard->has_album_node && gtk_tree_path_get_depth(path) > 1) {
724 gtk_tree_path_free(path);
725 return;
726 }
727
728 gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &titer, path);
729 }
730
731 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(clipboard->store), &iter, NULL, i++)) {
732
733 if (path == NULL) {
734 playlist_node_deep_copy(clipboard->store, &iter, pl->store, NULL, 2);
735 } else {
736 if (before) {
737 playlist_node_deep_copy(clipboard->store, &iter, pl->store, &titer, 1);
738 } else {
739 playlist_node_deep_copy(clipboard->store, &iter, pl->store, &titer, 0);
740 gtk_tree_model_iter_next(GTK_TREE_MODEL(pl->store), &titer);
741 }
742 }
743 }
744
745 if (path != NULL) {
746 gtk_tree_path_free(path);
747 }
748
749 playlist_content_changed(pl);
750 }
751
752
753 playlist_transfer_t *
playlist_transfer_new(playlist_t * pl)754 playlist_transfer_new(playlist_t * pl) {
755
756 playlist_transfer_t * pt = NULL;
757
758 if ((pt = (playlist_transfer_t *)calloc(1, sizeof(playlist_transfer_t))) == NULL) {
759 fprintf(stderr, "playlist_transfer_new(): calloc error\n");
760 return NULL;
761 }
762
763 if (pl == NULL) {
764 if ((pt->pl = playlist_get_current()) == NULL) {
765 free(pt);
766 return NULL;
767 }
768 } else {
769 pt->pl = pl;
770 }
771
772 pt->threshold = 1;
773
774 return pt;
775 }
776
777
778 void
playlist_transfer_free(playlist_transfer_t * pt)779 playlist_transfer_free(playlist_transfer_t * pt) {
780
781 if (pt->xml_ref != NULL) {
782 *(pt->xml_ref) -= 1;
783 if (*(pt->xml_ref) == 0) {
784 xmlFreeDoc((xmlDocPtr)pt->xml_doc);
785 free(pt->xml_ref);
786 }
787 } else if (pt->xml_doc != NULL) {
788 xmlFreeDoc((xmlDocPtr)pt->xml_doc);
789 }
790
791 if (pt->filename) {
792 free(pt->filename);
793 }
794
795 free(pt);
796 }
797
798 void
playlist_reset_display_names(void)799 playlist_reset_display_names(void) {
800
801 GList * node = NULL;
802 playlist_data_t * data = NULL;
803 char list_str[MAXLEN];
804
805 for (node = playlists; node; node = node->next) {
806 playlist_t * pl = (playlist_t *)node->data;
807 GtkTreeIter iter;
808 int i = 0;
809 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
810 if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pl->store), &iter)) {
811 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
812 if (!httpc_is_url(data->file)) {
813 playlist_data_get_display_name(list_str, data);
814 gtk_tree_store_set(pl->store, &iter, PL_COL_NAME, list_str, -1);
815 }
816 }
817 }
818 }
819 }
820
821 void
playlist_disable_bold_font_foreach(gpointer data,gpointer user_data)822 playlist_disable_bold_font_foreach(gpointer data, gpointer user_data) {
823
824 GtkTreeIter iter;
825 gint i = 0;
826
827 GtkTreeStore * store = ((playlist_t *)data)->store;
828
829 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, i++)) {
830 gint j = 0;
831 GtkTreeIter iter_child;
832
833 gtk_tree_store_set(store, &iter, PL_COL_FONT, PANGO_WEIGHT_NORMAL, -1);
834 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter_child, &iter, j++)) {
835 gtk_tree_store_set(store, &iter_child, PL_COL_FONT, PANGO_WEIGHT_NORMAL, -1);
836 }
837 }
838 }
839
840 void
playlist_disable_bold_font(void)841 playlist_disable_bold_font(void) {
842
843 g_list_foreach(playlists, playlist_disable_bold_font_foreach, NULL);
844 }
845
846 void
playlist_set_font_foreach(gpointer data,gpointer user_data)847 playlist_set_font_foreach(gpointer data, gpointer user_data) {
848
849 gtk_widget_modify_font(((playlist_t *)data)->view, fd_playlist);
850 }
851
852 void
playlist_set_font(int cond)853 playlist_set_font(int cond) {
854
855 if (cond) {
856 gtk_widget_modify_font(statusbar_selected, fd_statusbar);
857 gtk_widget_modify_font(statusbar_selected_label, fd_statusbar);
858 gtk_widget_modify_font(statusbar_total, fd_statusbar);
859 gtk_widget_modify_font(statusbar_total_label, fd_statusbar);
860
861 g_list_foreach(playlists, playlist_set_font_foreach, NULL);
862 }
863 }
864
865 void
playlist_set_markup(playlist_t * pl)866 playlist_set_markup(playlist_t * pl) {
867
868 if (pl->playing) {
869 gchar * str = g_markup_printf_escaped("<b>%s</b>", pl->name);
870 gtk_label_set_markup(GTK_LABEL(pl->label), str);
871 g_free(str);
872 gtk_widget_modify_fg(pl->label, GTK_STATE_NORMAL,
873 &playlist_color_indicator->style->fg[GTK_STATE_ACTIVE]);
874 gtk_widget_modify_fg(pl->label, GTK_STATE_ACTIVE,
875 &playlist_color_indicator->style->fg[GTK_STATE_ACTIVE]);
876 } else {
877 gtk_label_set_text(GTK_LABEL(pl->label), pl->name);
878 gtk_widget_modify_fg(pl->label, GTK_STATE_NORMAL,
879 &playlist_color_indicator->style->fg[GTK_STATE_NORMAL]);
880 gtk_widget_modify_fg(pl->label, GTK_STATE_ACTIVE,
881 &playlist_color_indicator->style->fg[GTK_STATE_NORMAL]);
882 }
883 }
884
885 void
playlist_set_playing(playlist_t * pl,int playing)886 playlist_set_playing(playlist_t * pl, int playing) {
887
888 pl->playing = playing;
889 playlist_set_markup(pl);
890 }
891
892 void
adjust_playlist_item_color(GtkTreeStore * store,GtkTreeIter * iter,char * active,char * inactive)893 adjust_playlist_item_color(GtkTreeStore * store, GtkTreeIter * iter,
894 char * active, char * inactive) {
895
896 playlist_data_t * data;
897
898 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, PL_COL_DATA, &data, -1);
899
900 if (IS_PL_ACTIVE(data)) {
901 gtk_tree_store_set(store, iter, PL_COL_COLO, active, -1);
902 if (options.show_active_track_name_in_bold) {
903 gtk_tree_store_set(store, iter, PL_COL_FONT, PANGO_WEIGHT_BOLD, -1);
904 }
905 } else {
906 gtk_tree_store_set(store, iter,
907 PL_COL_COLO, inactive,
908 PL_COL_FONT, PANGO_WEIGHT_NORMAL, -1);
909 }
910 }
911
912
913 void
adjust_playlist_color(playlist_t * pl,char * active,char * inactive)914 adjust_playlist_color(playlist_t * pl, char * active, char * inactive) {
915
916 int i = 0;
917 GtkTreeIter iter;
918
919 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
920 int j = 0;
921 GtkTreeIter iter_child;
922
923 adjust_playlist_item_color(pl->store, &iter, active, inactive);
924 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store),
925 &iter_child, &iter, j++)) {
926 adjust_playlist_item_color(pl->store, &iter_child,
927 active, inactive);
928 }
929 }
930 }
931
932 void
playlist_set_color(void)933 playlist_set_color(void) {
934
935 GList * node = NULL;
936
937 char active[14];
938 char inactive[14];
939
940 GdkColor color;
941
942 int rs = 0, gs = 0, bs = 0;
943 int ri = 0, gi = 0, bi = 0;
944
945
946 if (options.override_skin_settings &&
947 (gdk_color_parse(options.activesong_color, &color) == TRUE)) {
948
949 rs = color.red;
950 gs = color.green;
951 bs = color.blue;
952
953 if (rs == 0 && gs == 0 && bs == 0) {
954 rs = 1;
955 }
956 } else {
957 rs = playlist_color_indicator->style->fg[GTK_STATE_SELECTED].red;
958 gs = playlist_color_indicator->style->fg[GTK_STATE_SELECTED].green;
959 bs = playlist_color_indicator->style->fg[GTK_STATE_SELECTED].blue;
960 }
961
962 if (options.override_skin_settings &&
963 (gdk_color_parse(options.song_color, &color) == TRUE)) {
964
965 ri = color.red;
966 gi = color.green;
967 bi = color.blue;
968
969 } else {
970 ri = playlist_color_indicator->style->fg[GTK_STATE_INSENSITIVE].red;
971 gi = playlist_color_indicator->style->fg[GTK_STATE_INSENSITIVE].green;
972 bi = playlist_color_indicator->style->fg[GTK_STATE_INSENSITIVE].blue;
973 }
974
975 sprintf(active, "#%04X%04X%04X", rs, gs, bs);
976 sprintf(inactive, "#%04X%04X%04X", ri, gi, bi);
977
978 for (node = playlists; node; node = node->next) {
979 playlist_t * pl = (playlist_t *)node->data;
980 adjust_playlist_color(pl, active, inactive);
981 playlist_set_playing(pl, pl->playing);
982 }
983
984 for (node = playlists_closed; node; node = node->next) {
985 playlist_t * pl = (playlist_t *)node->data;
986 adjust_playlist_color(pl, active, inactive);
987 }
988
989 strcpy(pl_color_active, active);
990 strcpy(pl_color_inactive, inactive);
991 }
992
993 void
playlist_foreach_selected(playlist_t * pl,int (* foreach)(playlist_t *,GtkTreeIter *,void *),void * data)994 playlist_foreach_selected(playlist_t * pl, int (* foreach)(playlist_t *, GtkTreeIter *, void *),
995 void * data) {
996
997 GtkTreeIter iter_top;
998 GtkTreeIter iter;
999 gint i;
1000 gint j;
1001
1002 i = 0;
1003 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_top, NULL, i++)) {
1004
1005 gboolean topsel = gtk_tree_selection_iter_is_selected(pl->select, &iter_top);
1006
1007 if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pl->store), &iter_top)) {
1008
1009 j = 0;
1010 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, &iter_top, j++)) {
1011 if (topsel || gtk_tree_selection_iter_is_selected(pl->select, &iter)) {
1012 if (foreach(pl, &iter, data)) {
1013 return;
1014 }
1015 }
1016 }
1017
1018 } else if (topsel) {
1019 if (foreach(pl, &iter_top, data)) {
1020 return;
1021 }
1022 }
1023 }
1024 }
1025
1026 int
playlist_selection_file_types_foreach(playlist_t * pl,GtkTreeIter * iter,void * user_data)1027 playlist_selection_file_types_foreach(playlist_t * pl, GtkTreeIter * iter, void * user_data) {
1028
1029 playlist_data_t * pldata;
1030 int * i = (int *)user_data;
1031
1032 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &pldata, -1);
1033
1034 if (g_str_has_prefix(pldata->file, "CDDA ")) {
1035 *i = 1;
1036 } else if (!httpc_is_url(pldata->file)) {
1037 *i = 0;
1038 }
1039
1040 return *i == 0;
1041 }
1042
1043 /* return: 0: selection has audio file; 1: has cdda but no audio; 2: has only http or empty */
1044 int
playlist_selection_file_types(playlist_t * pl)1045 playlist_selection_file_types(playlist_t * pl) {
1046
1047 int type = 2;
1048
1049 playlist_foreach_selected(pl, playlist_selection_file_types_foreach, &type);
1050
1051 return type;
1052 }
1053
1054 GtkTreePath *
playlist_get_playing_path(playlist_t * pl)1055 playlist_get_playing_path(playlist_t * pl) {
1056
1057 playlist_data_t * data;
1058 GtkTreeIter iter;
1059 int i = 0;
1060
1061 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
1062
1063 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
1064
1065 if (IS_PL_ACTIVE(data)) {
1066 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter) > 0) {
1067
1068 playlist_data_t * data_child;
1069 GtkTreeIter iter_child;
1070 int j = 0;
1071
1072 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store),
1073 &iter_child, &iter, j++)) {
1074
1075 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter_child,
1076 PL_COL_DATA, &data_child, -1);
1077
1078 if (IS_PL_ACTIVE(data_child)) {
1079 return gtk_tree_model_get_path(GTK_TREE_MODEL(pl->store), &iter_child);
1080 }
1081 }
1082 } else {
1083 return gtk_tree_model_get_path(GTK_TREE_MODEL(pl->store), &iter);
1084 }
1085 }
1086 }
1087
1088 return NULL;
1089 }
1090
1091 void
mark_track(playlist_t * pl,GtkTreeIter * piter)1092 mark_track(playlist_t * pl, GtkTreeIter * piter) {
1093
1094 int j, n;
1095 char * name;
1096 char counter[MAXLEN];
1097 char tmpname[MAXLEN];
1098
1099 GtkTreeModel * model = GTK_TREE_MODEL(pl->store);
1100 playlist_data_t * data;
1101
1102
1103 gtk_tree_model_get(model, piter, PL_COL_DATA, &data, -1);
1104 if (IS_PL_ACTIVE(data)) {
1105 return;
1106 }
1107
1108
1109 n = gtk_tree_model_iter_n_children(model, piter);
1110
1111 if (n) {
1112 GtkTreeIter iter_child;
1113 playlist_data_t * data_child;
1114
1115 gtk_tree_model_get(model, piter, PL_COL_NAME, &name, -1);
1116 strncpy(tmpname, name, MAXLEN-1);
1117
1118 j = 0;
1119 while (gtk_tree_model_iter_nth_child(model, &iter_child, piter, j++)) {
1120
1121 gtk_tree_model_get(model, &iter_child, PL_COL_DATA, &data_child, -1);
1122 if (IS_PL_ACTIVE(data_child)) {
1123 break;
1124 }
1125 }
1126
1127 if (j > n) {
1128 return;
1129 }
1130
1131 sprintf(counter, _(" (%d/%d)"), j, n);
1132 strncat(tmpname, counter, MAXLEN-1);
1133 gtk_tree_store_set(pl->store, piter, PL_COL_NAME, tmpname, -1);
1134 data->ntracks = n;
1135 data->actrack = j;
1136 g_free(name);
1137 }
1138
1139 playlist_set_active(pl->store, piter);
1140
1141 if (gtk_tree_store_iter_depth(pl->store, piter)) { /* track node of album */
1142 GtkTreeIter iter_parent;
1143
1144 gtk_tree_model_iter_parent(model, &iter_parent, piter);
1145 mark_track(pl, &iter_parent);
1146 }
1147
1148 if (options.auto_roll_to_active_track) {
1149 roll_to_active_track(pl, piter);
1150 }
1151 }
1152
1153
1154 void
unmark_track(playlist_t * pl,GtkTreeIter * piter)1155 unmark_track(playlist_t * pl, GtkTreeIter * piter) {
1156
1157 int n;
1158 GtkTreeModel * model = GTK_TREE_MODEL(pl->store);
1159
1160 playlist_set_inactive(pl->store, piter);
1161
1162 n = gtk_tree_model_iter_n_children(model, piter);
1163
1164 if (n) {
1165 char name[MAXLEN];
1166 playlist_data_t * data;
1167
1168 gtk_tree_model_get(model, piter, PL_COL_DATA, &data, -1);
1169 snprintf(name, MAXLEN-1, "%s: %s", data->artist, data->album);
1170 gtk_tree_store_set(pl->store, piter, PL_COL_NAME, name, -1);
1171 }
1172
1173 if (gtk_tree_store_iter_depth(pl->store, piter)) { /* track node of album */
1174 GtkTreeIter iter_parent;
1175
1176 gtk_tree_model_iter_parent(model, &iter_parent, piter);
1177 unmark_track(pl, &iter_parent);
1178 }
1179 }
1180
1181
1182 void
playlist_start_playback_at_path(playlist_t * pl,GtkTreePath * path)1183 playlist_start_playback_at_path(playlist_t * pl, GtkTreePath * path) {
1184
1185 GtkTreeIter iter;
1186 GtkTreePath * p;
1187 gchar cmd;
1188 cue_t cue;
1189
1190 playlist_t * plist = NULL;
1191 playlist_data_t * pldata = NULL;
1192 GtkTreeModel * model = GTK_TREE_MODEL(pl->store);
1193
1194
1195 if (!allow_seeks) {
1196 return;
1197 }
1198
1199 while ((plist = playlist_get_playing()) != NULL) {
1200 playlist_set_playing(plist, 0);
1201 }
1202
1203 playlist_set_playing(pl, 1);
1204
1205 while ((p = playlist_get_playing_path(pl)) != NULL) {
1206 gtk_tree_model_get_iter(model, &iter, p);
1207 gtk_tree_path_free(p);
1208 unmark_track(pl, &iter);
1209 }
1210
1211 gtk_tree_model_get_iter(model, &iter, path);
1212
1213 if (gtk_tree_model_iter_n_children(model, &iter) > 0) {
1214 GtkTreeIter iter_child;
1215 gtk_tree_model_iter_children(model, &iter_child, &iter);
1216 mark_track(pl, &iter_child);
1217 } else {
1218 mark_track(pl, &iter);
1219 }
1220
1221 p = playlist_get_playing_path(pl);
1222 gtk_tree_model_get_iter(model, &iter, p);
1223 gtk_tree_path_free(p);
1224
1225 gtk_tree_model_get(model, &iter, PL_COL_DATA, &pldata, -1);
1226 cue_track_for_playback(pl->store, &iter, &cue);
1227
1228 is_file_loaded = 1;
1229
1230 if (options.show_sn_title) {
1231 refresh_displays();
1232 }
1233
1234 toggle_noeffect(PLAY, TRUE);
1235
1236 if (is_paused) {
1237 is_paused = 0;
1238 toggle_noeffect(PAUSE, FALSE);
1239 }
1240
1241 cmd = CMD_CUE;
1242 flush_rb_disk2gui();
1243 rb_write(rb_gui2disk, &cmd, sizeof(char));
1244 rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
1245 try_waking_disk_thread();
1246 }
1247
1248
1249 static gboolean
playlist_window_close(GtkWidget * widget,GdkEvent * event,gpointer data)1250 playlist_window_close(GtkWidget * widget, GdkEvent * event, gpointer data) {
1251
1252 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_toggle), FALSE);
1253
1254 return TRUE;
1255 }
1256
1257
1258 gint
playlist_window_key_pressed(GtkWidget * widget,GdkEventKey * kevent)1259 playlist_window_key_pressed(GtkWidget * widget, GdkEventKey * kevent) {
1260
1261 GtkTreePath * path;
1262 GtkTreeIter iter;
1263 playlist_t * pl;
1264
1265 if ((pl = playlist_get_current()) == NULL) {
1266 return TRUE;
1267 }
1268
1269 switch (kevent->keyval) {
1270
1271 case GDK_Insert:
1272 case GDK_KP_Insert:
1273 if (kevent->state & GDK_SHIFT_MASK) { /* SHIFT + Insert */
1274 add_directory(NULL, NULL);
1275 } else {
1276 add_files(NULL, NULL);
1277 }
1278 return TRUE;
1279 case GDK_q:
1280 case GDK_Q:
1281 case GDK_Escape:
1282 if (!options.playlist_is_embedded) {
1283 playlist_window_close(NULL, NULL, NULL);
1284 }
1285 return TRUE;
1286 case GDK_F1:
1287 case GDK_i:
1288 case GDK_I:
1289 gtk_tree_view_get_cursor(GTK_TREE_VIEW(pl->view), &path, NULL);
1290
1291 if (path &&
1292 gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, path) &&
1293 !gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pl->store), &iter)) {
1294 playlist_data_t * data;
1295 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
1296 show_file_info(GTK_TREE_MODEL(pl->store), iter, playlist_model_func,
1297 0, FALSE, IS_PL_COVER(data));
1298 }
1299
1300 if (path) {
1301 gtk_tree_path_free(path);
1302 }
1303
1304 return TRUE;
1305 case GDK_Return:
1306 case GDK_KP_Enter:
1307 gtk_tree_view_get_cursor(GTK_TREE_VIEW(pl->view), &path, NULL);
1308
1309 if (path) {
1310 playlist_start_playback_at_path(pl, path);
1311 gtk_tree_path_free(path);
1312 }
1313 return TRUE;
1314 case GDK_u:
1315 case GDK_U:
1316 cut__sel_cb(NULL);
1317 return TRUE;
1318 case GDK_f:
1319 case GDK_F:
1320 plist__search_cb(pl);
1321 return TRUE;
1322 case GDK_a:
1323 case GDK_A:
1324 if (kevent->state & GDK_CONTROL_MASK) {
1325 if (kevent->state & GDK_SHIFT_MASK) {
1326 sel__none_cb(NULL);
1327 } else {
1328 sel__all_cb(NULL);
1329 }
1330 } else {
1331 if (kevent->state & GDK_MOD1_MASK) { /* ALT + a */
1332 playlist_set_current(playlist_get_playing());
1333 show_active_position_in_playlist(playlist_get_playing());
1334 } else {
1335 plist__roll_cb(pl);
1336 }
1337 }
1338 return TRUE;
1339 case GDK_w:
1340 case GDK_W:
1341 if (kevent->state & GDK_CONTROL_MASK) {
1342 playlist_tab_close(NULL, pl);
1343 } else {
1344 gtk_tree_view_collapse_all(GTK_TREE_VIEW(pl->view));
1345 show_active_position_in_playlist(pl);
1346 }
1347 return TRUE;
1348 case GDK_Delete:
1349 case GDK_KP_Delete:
1350 if (kevent->state & GDK_SHIFT_MASK) { /* SHIFT + Delete */
1351 rem__all_cb(NULL);
1352 } else if (kevent->state & GDK_CONTROL_MASK) { /* CTRL + Delete */
1353 playlist_unlink_files(pl);
1354 } else {
1355 rem__sel_cb(NULL);
1356 }
1357 return TRUE;
1358 case GDK_t:
1359 case GDK_T:
1360 if (kevent->state & GDK_CONTROL_MASK) {
1361 if (kevent->state & GDK_SHIFT_MASK) { /* CTRL + SHIFT T */
1362 playlist_tab_close_undo();
1363 } else { /* CTRL + T */
1364 playlist_tab_new(NULL);
1365 }
1366 }
1367 #ifdef HAVE_IFP
1368 else {
1369 if (kevent->state & GDK_SHIFT_MASK) { /* SHIFT T */
1370 aifp_transfer_files(DOWNLOAD_MODE);
1371 } else {
1372 aifp_transfer_files(UPLOAD_MODE);
1373 }
1374 }
1375 #endif /* HAVE_IFP */
1376 return TRUE;
1377 case GDK_r:
1378 case GDK_R:
1379 if (kevent->state & GDK_CONTROL_MASK) { /* CTRL + R */
1380 tab__rename_cb(playlist_get_current());
1381 }
1382 return TRUE;
1383 case GDK_grave:
1384 expand_collapse_album_node(pl);
1385 return TRUE;
1386 case GDK_Page_Up:
1387 if (kevent->state & GDK_CONTROL_MASK) {
1388 playlist_notebook_prev_page();
1389 return TRUE;
1390 }
1391 break;
1392 case GDK_Page_Down:
1393 if (kevent->state & GDK_CONTROL_MASK) {
1394 playlist_notebook_next_page();
1395 return TRUE;
1396 }
1397 break;
1398 case GDK_ISO_Left_Tab: /* it is usually mapped to SHIFT + TAB */
1399 if (kevent->state & GDK_CONTROL_MASK) {
1400 playlist_notebook_prev_page();
1401 }
1402 return TRUE;
1403 case GDK_Tab:
1404 if (kevent->state & GDK_CONTROL_MASK) {
1405 if (kevent->state & GDK_SHIFT_MASK) {
1406 playlist_notebook_prev_page();
1407 } else {
1408 playlist_notebook_next_page();
1409 }
1410 }
1411 return TRUE;
1412 case GDK_c:
1413 case GDK_C:
1414 if (kevent->state & GDK_CONTROL_MASK) {
1415 playlist_copy(playlist_get_current());
1416 return TRUE;
1417 }
1418 break;
1419 case GDK_x:
1420 case GDK_X:
1421 if (kevent->state & GDK_CONTROL_MASK) {
1422 playlist_cut(playlist_get_current());
1423 return TRUE;
1424 }
1425 break;
1426 case GDK_v:
1427 case GDK_V:
1428 if (kevent->state & GDK_CONTROL_MASK) {
1429 if (kevent->state & GDK_SHIFT_MASK) {
1430 playlist_paste(playlist_get_current(), 0/*after*/);
1431 } else {
1432 playlist_paste(playlist_get_current(), 1/*before*/);
1433 }
1434 return TRUE;
1435 }
1436 break;
1437 }
1438
1439 return FALSE;
1440 }
1441
1442 gint
playlist_notebook_key_pressed(GtkWidget * widget,GdkEventKey * kevent)1443 playlist_notebook_key_pressed(GtkWidget * widget, GdkEventKey * kevent) {
1444
1445 if ((kevent->state & GDK_CONTROL_MASK) &&
1446 (kevent->keyval == GDK_Page_Up || kevent->keyval == GDK_Page_Down)) {
1447 /* ignore default tab switching key handler */
1448 return TRUE;
1449 }
1450
1451 if (kevent->keyval == GDK_Tab ||
1452 kevent->keyval == GDK_ISO_Left_Tab) {
1453 /* prevent focus traversal */
1454 return TRUE;
1455 }
1456
1457 return FALSE;
1458 }
1459
1460 gint
playlist_key_pressed(GtkWidget * widget,GdkEventKey * kevent)1461 playlist_key_pressed(GtkWidget * widget, GdkEventKey * kevent) {
1462
1463 if ((kevent->state & GDK_CONTROL_MASK) &&
1464 (kevent->keyval == GDK_Page_Up || kevent->keyval == GDK_Page_Down)) {
1465 /* ignore default key handler for PageUp/Down when
1466 CTRL is pressed to prevent unwanted cursor motion */
1467 return TRUE;
1468 }
1469
1470 if (kevent->keyval == GDK_Tab ||
1471 kevent->keyval == GDK_ISO_Left_Tab) {
1472 /* prevent focus traversal */
1473 return TRUE;
1474 }
1475
1476 return FALSE;
1477 }
1478
1479 void
playlist_menu_set_popup_sensitivity(playlist_t * pl)1480 playlist_menu_set_popup_sensitivity(playlist_t * pl) {
1481
1482 int file_types = 0;
1483 int has_selection = (gtk_tree_selection_count_selected_rows(pl->select) != 0);
1484
1485 if (has_selection) {
1486 file_types = playlist_selection_file_types(pl);
1487 }
1488
1489 gtk_widget_set_sensitive(plist__reread_file_meta, has_selection && (file_types == 0));
1490 gtk_widget_set_sensitive(plist__rva, has_selection && (file_types <= 1));
1491 #ifdef HAVE_TRANSCODING
1492 gtk_widget_set_sensitive(plist__export, has_selection && (file_types == 0));
1493 #endif /* HAVE_TRANSCODING */
1494 #ifdef HAVE_IFP
1495 gtk_widget_set_sensitive(plist__send_songs_to_iriver, has_selection && (file_types == 0));
1496 #endif /* HAVE_IFP */
1497 }
1498
1499 gint
playlist_window_button_pressed(GtkWidget * widget,GdkEventButton * event,gpointer data)1500 playlist_window_button_pressed(GtkWidget * widget, GdkEventButton * event, gpointer data) {
1501
1502 GtkTreePath * path;
1503 GtkTreeIter iter;
1504 playlist_t * pl = (playlist_t *)data;
1505
1506 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
1507
1508 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(pl->view), event->x, event->y,
1509 &path, NULL, NULL, NULL)) {
1510 playlist_start_playback_at_path(pl, path);
1511 gtk_tree_path_free(path);
1512 }
1513 return TRUE;
1514 }
1515
1516 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
1517 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(pl->view), event->x, event->y,
1518 &path, NULL, NULL, NULL) &&
1519 gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, path)) {
1520 if (gtk_tree_selection_count_selected_rows(pl->select) == 0) {
1521 gtk_tree_view_set_cursor(GTK_TREE_VIEW(pl->view), path, NULL, FALSE);
1522 }
1523 if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pl->store), &iter)) {
1524 gtk_widget_set_sensitive(plist__fileinfo, FALSE);
1525 } else {
1526 gtk_widget_set_sensitive(plist__fileinfo, TRUE);
1527 fileinfo_iter = gtk_tree_iter_copy(&iter);
1528 }
1529 } else {
1530 gtk_widget_set_sensitive(plist__fileinfo, TRUE);
1531 }
1532
1533 playlist_menu_set_popup_sensitivity(pl);
1534
1535 gtk_menu_popup(GTK_MENU(plist_menu), NULL, NULL, NULL, NULL,
1536 event->button, event->time);
1537 return TRUE;
1538 }
1539 return FALSE;
1540 }
1541
1542
1543 static int
filter(const struct dirent * de)1544 filter(const struct dirent * de) {
1545
1546 return de->d_name[0] != '.';
1547 }
1548
1549
1550 gboolean
finalize_add_to_playlist(gpointer data)1551 finalize_add_to_playlist(gpointer data) {
1552
1553 playlist_transfer_t * pt = (playlist_transfer_t *)data;
1554
1555 pt->pl->progbar_semaphore--;
1556
1557 if (pt->pl->progbar_semaphore != 0) {
1558 return FALSE;
1559 }
1560
1561 if (playlist_window == NULL) {
1562 return FALSE;
1563 }
1564
1565 if (pt->pl == playlist_get_current()) {
1566 playlist_content_changed(pt->pl);
1567 if (pt->start_playback) {
1568 GtkTreeModel * model = GTK_TREE_MODEL(pt->pl->store);
1569 GtkTreePath * path = NULL;
1570 GtkTreeIter iter;
1571
1572 if (gtk_tree_model_get_iter_first(model, &iter)) {
1573 if (gtk_tree_model_iter_has_child(model, &iter)) {
1574 GtkTreeIter iter_child;
1575 gtk_tree_model_iter_children(model, &iter_child, &iter);
1576 path = gtk_tree_model_get_path(model, &iter_child);
1577 } else {
1578 path = gtk_tree_model_get_path(model, &iter);
1579 }
1580 }
1581 if (path != NULL) {
1582 playlist_start_playback_at_path(pt->pl, path);
1583 gtk_tree_path_free(path);
1584 }
1585 }
1586 }
1587
1588 playlist_progress_bar_hide(pt->pl);
1589
1590 if (pt->xml_ref != NULL) {
1591 select_active_position_in_playlist(pt->pl);
1592 }
1593
1594 return FALSE;
1595 }
1596
1597 void
playlist_data_get_display_name(char * list_str,playlist_data_t * pldata)1598 playlist_data_get_display_name(char * list_str, playlist_data_t * pldata) {
1599
1600 if (pldata->display) {
1601 strncpy(list_str, pldata->display, MAXLEN-1);
1602 } else if (pldata->artist || pldata->album || pldata->title) {
1603 make_title_string(list_str, options.title_format,
1604 pldata->artist, pldata->album, pldata->title);
1605 } else {
1606 gchar * tmp = g_filename_display_name(pldata->file);
1607 if (options.meta_use_basename_only) {
1608 char * bname = g_path_get_basename(tmp);
1609 strncpy(list_str, bname, MAXLEN-1);
1610 g_free(bname);
1611 } else {
1612 strncpy(list_str, tmp, MAXLEN-1);
1613 }
1614 g_free(tmp);
1615
1616 if (options.meta_us_to_space) {
1617 int i;
1618 for (i = 0; list_str[i]; i++) {
1619 if (list_str[i] == '_') {
1620 list_str[i] = ' ';
1621 }
1622 }
1623 }
1624 if (options.meta_rm_extension) {
1625 char * c = NULL;
1626 if ((c = strrchr(list_str, '.')) != NULL) {
1627 *c = '\0';
1628 }
1629 }
1630 }
1631 }
1632
1633 gboolean
add_file_to_playlist(gpointer data)1634 add_file_to_playlist(gpointer data) {
1635
1636 playlist_transfer_t * pt = (playlist_transfer_t *)data;
1637 GList * node;
1638 int finish = 0;
1639
1640 AQUALUNG_MUTEX_LOCK(pt->pl->wait_mutex);
1641
1642 if (pt->clear) {
1643 rem_all(pt->pl);
1644 pt->clear = 0;
1645 }
1646
1647 for (node = pt->pldata_list; node; node = node->next) {
1648
1649 GtkTreeIter iter;
1650 GtkTreePath * path;
1651 char voladj_str[32];
1652 char duration_str[MAXLEN];
1653 char list_str[MAXLEN];
1654
1655 playlist_data_t * pldata = (playlist_data_t *)node->data;
1656
1657 pt->data_written--;
1658
1659 if (pldata == NULL) {
1660 pt->data_written = 0;
1661 finish = 1;
1662 break;
1663 }
1664
1665 voladj2str(pldata->voladj, voladj_str);
1666 time2time_na(pldata->duration, duration_str);
1667
1668 if (IS_PL_TOPLEVEL(pldata)) {
1669 gtk_tree_store_append(pt->pl->store, &iter, NULL);
1670 } else {
1671 GtkTreeIter parent;
1672 int n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pt->pl->store), NULL);
1673
1674 if (n == 0) {
1675 /* someone viciously cleared the list while adding tracks to album node;
1676 ignore further tracks added to this node */
1677 playlist_data_free(pldata);
1678 continue;
1679 }
1680
1681 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pt->pl->store), &parent, NULL, n-1);
1682 gtk_tree_store_append(pt->pl->store, &iter, &parent);
1683 }
1684
1685 if (IS_PL_ALBUM_NODE(pldata)) {
1686 if (IS_PL_ACTIVE(pldata)) {
1687 snprintf(list_str, MAXLEN-1, "%s: %s (%d/%d)",
1688 pldata->artist, pldata->album,
1689 pldata->actrack, pldata->ntracks);
1690 } else {
1691 snprintf(list_str, MAXLEN-1, "%s: %s",
1692 pldata->artist, pldata->album);
1693 }
1694 } else if (IS_PL_ALBUM_CHILD(pldata)) {
1695 GtkTreeIter parent;
1696 playlist_data_t * pdata;
1697 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(pt->pl->store), &parent, &iter)) {
1698 gtk_tree_model_get(GTK_TREE_MODEL(pt->pl->store), &parent, PL_COL_DATA, &pdata, -1);
1699 if (pdata->artist && pdata->album && pldata->artist && pldata->album && pldata->title &&
1700 !strcmp(pdata->artist, pldata->artist) && !strcmp(pdata->album, pldata->album)) {
1701 strncpy(list_str, pldata->title, MAXLEN-1);
1702 } else {
1703 playlist_data_get_display_name(list_str, pldata);
1704 }
1705 }
1706 } else {
1707 playlist_data_get_display_name(list_str, pldata);
1708 }
1709
1710 gtk_tree_store_set(pt->pl->store, &iter,
1711 PL_COL_NAME, list_str,
1712 PL_COL_VADJ, IS_PL_ALBUM_NODE(pldata) ? "" : voladj_str,
1713 PL_COL_DURA, duration_str,
1714 PL_COL_COLO, IS_PL_ACTIVE(pldata) ? pl_color_active : pl_color_inactive,
1715 PL_COL_FONT, (IS_PL_ACTIVE(pldata) && options.show_active_track_name_in_bold) ?
1716 PANGO_WEIGHT_BOLD :
1717 PANGO_WEIGHT_NORMAL,
1718 PL_COL_DATA, pldata,
1719 -1);
1720
1721 if (!pt->start_playback && IS_PL_ACTIVE(pldata) && !IS_PL_ALBUM_NODE(pldata) &&
1722 (path = playlist_get_playing_path(pt->pl)) != NULL) {
1723 /* deactivate item if playing path already exists */
1724 GtkTreePath * p = gtk_tree_model_get_path(GTK_TREE_MODEL(pt->pl->store), &iter);
1725 if (gtk_tree_path_compare(path, p) != 0) {
1726 unmark_track(pt->pl, &iter);
1727 }
1728 gtk_tree_path_free(path);
1729 gtk_tree_path_free(p);
1730 }
1731
1732 if (pt->start_playback && IS_PL_ACTIVE(pldata) && !IS_PL_ALBUM_NODE(pldata)) {
1733 GtkTreePath * p = gtk_tree_model_get_path(GTK_TREE_MODEL(pt->pl->store), &iter);
1734 GtkTreeIter parent;
1735
1736 /* save/restore the displayed name and ntracks of an album node before/after starting
1737 playback at one of its childlen, since the album node may not yet be fully loaded */
1738 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(pt->pl->store), &parent, &iter)) {
1739 gchar * name;
1740 unsigned short ntracks;
1741 playlist_data_t * pdata;
1742
1743 gtk_tree_model_get(GTK_TREE_MODEL(pt->pl->store), &parent,
1744 PL_COL_NAME, &name,
1745 PL_COL_DATA, &pdata, -1);
1746 ntracks = pdata->ntracks;
1747
1748 playlist_start_playback_at_path(pt->pl, p);
1749
1750 gtk_tree_store_set(pt->pl->store, &parent, PL_COL_NAME, name, -1);
1751 pdata->ntracks = ntracks;
1752 g_free(name);
1753 } else {
1754 playlist_start_playback_at_path(pt->pl, p);
1755 }
1756
1757 gtk_tree_path_free(p);
1758 pt->start_playback = 0;
1759 }
1760 }
1761
1762 g_list_free(pt->pldata_list);
1763 pt->pldata_list = NULL;
1764
1765 if (finish) {
1766 finalize_add_to_playlist(pt);
1767 }
1768
1769 AQUALUNG_MUTEX_UNLOCK(pt->pl->wait_mutex);
1770 AQUALUNG_COND_SIGNAL(pt->pl->thread_wait);
1771
1772 return FALSE;
1773 }
1774
1775
1776 void
playlist_thread_add_to_list(playlist_transfer_t * pt,playlist_data_t * pldata)1777 playlist_thread_add_to_list(playlist_transfer_t * pt, playlist_data_t * pldata) {
1778
1779 AQUALUNG_MUTEX_LOCK(pt->pl->wait_mutex);
1780
1781 pt->pldata_list = g_list_append(pt->pldata_list, pldata);
1782 pt->data_written++;
1783
1784 if (pt->data_written >= pt->threshold || pldata == NULL) {
1785
1786 if (pt->threshold < 64) {
1787 pt->threshold *= 2;
1788 }
1789
1790 aqualung_idle_add(add_file_to_playlist, pt);
1791
1792 while (pt->data_written > 0) {
1793 AQUALUNG_COND_WAIT(pt->pl->thread_wait, pt->pl->wait_mutex);
1794 }
1795 }
1796
1797 AQUALUNG_MUTEX_UNLOCK(pt->pl->wait_mutex);
1798 }
1799
1800 void *
add_files_to_playlist_thread(void * arg)1801 add_files_to_playlist_thread(void * arg) {
1802
1803 playlist_transfer_t * pt = (playlist_transfer_t *)arg;
1804 GSList * node = NULL;
1805
1806 AQUALUNG_THREAD_DETACH();
1807
1808 AQUALUNG_MUTEX_LOCK(pt->pl->thread_mutex);
1809
1810 for (node = pt->list; node; node = node->next) {
1811
1812 if (!pt->pl->thread_stop) {
1813 playlist_data_t * pldata = NULL;
1814
1815 if ((pldata = playlist_filemeta_get((char *)node->data)) != NULL) {
1816 if (pt->start_playback) {
1817 PL_SET_FLAG(pldata, PL_FLAG_ACTIVE);
1818 }
1819 playlist_thread_add_to_list(pt, pldata);
1820 }
1821 }
1822
1823 g_free(node->data);
1824 }
1825
1826 playlist_thread_add_to_list(pt, NULL);
1827
1828 g_slist_free(pt->list);
1829
1830 AQUALUNG_MUTEX_UNLOCK(pt->pl->thread_mutex);
1831
1832 playlist_transfer_free(pt);
1833
1834 return NULL;
1835 }
1836
1837
1838 void
add_files(GtkWidget * widget,gpointer data)1839 add_files(GtkWidget * widget, gpointer data) {
1840
1841 GSList * files = file_chooser(_("Select files"),
1842 options.playlist_is_embedded ? main_window : playlist_window,
1843 GTK_FILE_CHOOSER_ACTION_OPEN,
1844 FILE_CHOOSER_FILTER_AUDIO,
1845 TRUE,
1846 options.audiodir);
1847 if (files != NULL) {
1848
1849 playlist_transfer_t * pt = playlist_transfer_get(PLAYLIST_ENQUEUE, NULL, 0);
1850
1851 if (pt != NULL) {
1852 pt->list = files;
1853 playlist_progress_bar_show(pt->pl);
1854 AQUALUNG_THREAD_CREATE(pt->pl->thread_id,
1855 NULL,
1856 add_files_to_playlist_thread,
1857 pt);
1858 }
1859 }
1860 }
1861
1862
1863 void
add_dir_to_playlist(playlist_transfer_t * pt,char * dirname)1864 add_dir_to_playlist(playlist_transfer_t * pt, char * dirname) {
1865
1866 gint i, n;
1867 struct dirent ** ent;
1868 gchar path[MAXLEN];
1869
1870
1871 if (pt->pl->thread_stop) {
1872 return;
1873 }
1874
1875 n = scandir(dirname, &ent, filter, alphasort);
1876 for (i = 0; i < n; i++) {
1877
1878 if (pt->pl->thread_stop) {
1879 break;
1880 }
1881
1882 snprintf(path, MAXLEN-1, "%s/%s", dirname, ent[i]->d_name);
1883
1884 if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
1885 free(ent[i]);
1886 continue;
1887 }
1888
1889 if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
1890 add_dir_to_playlist(pt, path);
1891 } else {
1892 playlist_data_t * pldata = NULL;
1893
1894 if ((pldata = playlist_filemeta_get(path)) != NULL) {
1895 if (pt->start_playback) {
1896 PL_SET_FLAG(pldata, PL_FLAG_ACTIVE);
1897 }
1898 playlist_thread_add_to_list(pt, pldata);
1899 }
1900 }
1901
1902 free(ent[i]);
1903 }
1904
1905 while (i < n) {
1906 free(ent[i]);
1907 ++i;
1908 }
1909
1910 if (n > 0) {
1911 free(ent);
1912 }
1913 }
1914
1915
1916 void *
add_dir_to_playlist_thread(void * arg)1917 add_dir_to_playlist_thread(void * arg) {
1918
1919
1920 playlist_transfer_t * pt = (playlist_transfer_t *)arg;
1921 GSList * node = NULL;
1922
1923 AQUALUNG_THREAD_DETACH();
1924
1925 AQUALUNG_MUTEX_LOCK(pt->pl->thread_mutex);
1926
1927 for (node = pt->list; node; node = node->next) {
1928
1929 add_dir_to_playlist(pt, (char *)node->data);
1930 g_free(node->data);
1931 }
1932
1933 playlist_thread_add_to_list(pt, NULL);
1934
1935 g_slist_free(pt->list);
1936
1937 AQUALUNG_MUTEX_UNLOCK(pt->pl->thread_mutex);
1938
1939 playlist_transfer_free(pt);
1940
1941 return NULL;
1942 }
1943
1944
1945 void
add_directory(GtkWidget * widget,gpointer data)1946 add_directory(GtkWidget * widget, gpointer data) {
1947
1948 GSList * dirs = file_chooser(_("Select directory"),
1949 options.playlist_is_embedded ? main_window : playlist_window,
1950 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1951 FILE_CHOOSER_FILTER_NONE,
1952 TRUE,
1953 options.audiodir);
1954 if (dirs != NULL) {
1955
1956 playlist_transfer_t * pt = playlist_transfer_get(PLAYLIST_ENQUEUE, NULL, 0);
1957
1958 if (pt != NULL) {
1959 pt->list = dirs;
1960 playlist_progress_bar_show(pt->pl);
1961 AQUALUNG_THREAD_CREATE(pt->pl->thread_id,
1962 NULL,
1963 add_dir_to_playlist_thread, pt);
1964 }
1965 }
1966 }
1967
1968 gint
add_url_key_press_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)1969 add_url_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) {
1970
1971 if (event->keyval == GDK_Return) {
1972 gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_ACCEPT);
1973 return TRUE;
1974 }
1975
1976 return FALSE;
1977 }
1978
1979 void
add_url(GtkWidget * widget,gpointer data)1980 add_url(GtkWidget * widget, gpointer data) {
1981
1982 GtkWidget * dialog;
1983 GtkWidget * table;
1984 GtkWidget * hbox;
1985 GtkWidget * hbox2;
1986 GtkWidget * url_entry;
1987 GtkWidget * url_label;
1988 char url[MAXLEN];
1989
1990 dialog = gtk_dialog_new_with_buttons(_("Add URL"),
1991 options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
1992 GTK_DIALOG_DESTROY_WITH_PARENT,
1993 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1994 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1995 NULL);
1996
1997 gtk_widget_set_size_request(GTK_WIDGET(dialog), 400, -1);
1998 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
1999 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_REJECT);
2000 g_signal_connect (G_OBJECT (dialog), "key_press_event",
2001 G_CALLBACK (add_url_key_press_cb), NULL);
2002
2003 table = gtk_table_new(2, 2, FALSE);
2004 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
2005 table, FALSE, TRUE, 2);
2006
2007 url_label = gtk_label_new(_("URL:"));
2008 hbox = gtk_hbox_new(FALSE, 0);
2009 gtk_box_pack_start(GTK_BOX(hbox), url_label, FALSE, FALSE, 0);
2010 gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 5, 5);
2011
2012 hbox2 = gtk_hbox_new(FALSE, 0);
2013 gtk_table_attach(GTK_TABLE(table), hbox2, 1, 2, 0, 1,
2014 GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 5);
2015
2016 url_entry = gtk_entry_new();
2017 gtk_entry_set_max_length(GTK_ENTRY(url_entry), MAXLEN - 1);
2018 gtk_entry_set_text(GTK_ENTRY(url_entry), "http://");
2019 gtk_box_pack_start(GTK_BOX(hbox2), url_entry, TRUE, TRUE, 0);
2020
2021 gtk_widget_grab_focus(url_entry);
2022
2023 gtk_widget_show_all(dialog);
2024
2025 display:
2026 url[0] = '\0';
2027 if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2028
2029 GtkTreeIter iter;
2030 gchar voladj_str[32];
2031 gchar duration_str[MAXLEN];
2032 playlist_t * pl = playlist_get_current();
2033 playlist_data_t * data;
2034
2035 strncpy(url, gtk_entry_get_text(GTK_ENTRY(url_entry)), MAXLEN-1);
2036
2037 if (url[0] == '\0' || strstr(url, "http://") != url || strlen(url) <= strlen("http://")) {
2038 gtk_widget_grab_focus(url_entry);
2039 goto display;
2040 }
2041
2042 if ((data = playlist_data_new()) == NULL) {
2043 return;
2044 }
2045
2046 data->file = strdup(url);
2047 data->duration = 0.0f;
2048 data->voladj = options.rva_no_rva_voladj;
2049
2050 voladj2str(data->voladj, voladj_str);
2051 time2time_na(data->duration, duration_str);
2052
2053 gtk_tree_store_append(pl->store, &iter, NULL);
2054 gtk_tree_store_set(pl->store, &iter,
2055 PL_COL_NAME, url,
2056 PL_COL_VADJ, voladj_str,
2057 PL_COL_DURA, duration_str,
2058 PL_COL_COLO, pl_color_inactive,
2059 PL_COL_FONT, PANGO_WEIGHT_NORMAL,
2060 PL_COL_DATA, data, -1);
2061
2062 playlist_content_changed(pl);
2063 }
2064
2065 gtk_widget_destroy(dialog);
2066 }
2067
2068
2069 void
plist__save_cb(gpointer data)2070 plist__save_cb(gpointer data) {
2071
2072 GSList * file;
2073 playlist_t * pl;
2074
2075 if ((pl = playlist_get_current()) == NULL) {
2076 return;
2077 }
2078
2079 file = file_chooser(_("Please specify the file to save the playlist to."),
2080 options.playlist_is_embedded ? main_window : playlist_window,
2081 GTK_FILE_CHOOSER_ACTION_SAVE,
2082 FILE_CHOOSER_FILTER_NONE,
2083 FALSE,
2084 options.plistdir);
2085
2086 if (file != NULL) {
2087 if (g_str_has_suffix((gchar *)file->data, ".m3u")) {
2088 playlist_save_m3u(pl, (char *)file->data);
2089 }
2090 else {
2091 playlist_save(pl, (char *)file->data);
2092 }
2093 g_free(file->data);
2094 g_slist_free(file);
2095 }
2096 }
2097
2098
2099 void
plist__save_all_cb(gpointer data)2100 plist__save_all_cb(gpointer data) {
2101
2102 GSList * file;
2103 playlist_t * pl;
2104
2105 if ((pl = playlist_get_current()) == NULL) {
2106 return;
2107 }
2108
2109 file = file_chooser(_("Please specify the file to save the playlist to."),
2110 options.playlist_is_embedded ? main_window : playlist_window,
2111 GTK_FILE_CHOOSER_ACTION_SAVE,
2112 FILE_CHOOSER_FILTER_NONE,
2113 FALSE,
2114 options.plistdir);
2115 if (file != NULL) {
2116 playlist_save_all((char *)file->data);
2117 g_free(file->data);
2118 g_slist_free(file);
2119 }
2120 }
2121
2122
2123 playlist_transfer_t *
playlist_transfer_get(int mode,char * tab_name,int start_playback)2124 playlist_transfer_get(int mode, char * tab_name, int start_playback) {
2125
2126 playlist_t * pl = NULL;
2127 playlist_transfer_t * pt = NULL;
2128
2129 if (mode == PLAYLIST_LOAD_TAB) {
2130 pl = playlist_tab_new(tab_name);
2131 pt = playlist_transfer_new(pl);
2132 } else {
2133 if (tab_name == NULL) {
2134 pl = playlist_get_current();
2135 } else {
2136 pl = playlist_get_by_name(tab_name);
2137 }
2138 if (pl == NULL) {
2139 pl = playlist_tab_new(tab_name);
2140 }
2141 pt = playlist_transfer_new(pl);
2142 }
2143
2144 if (mode == PLAYLIST_LOAD) {
2145 pt->clear = 1;
2146 }
2147
2148 pt->start_playback = start_playback;
2149
2150 return pt;
2151 }
2152
2153
2154 void
playlist_load(GSList * list,int mode,char * tab_name,int start_playback)2155 playlist_load(GSList * list, int mode, char * tab_name, int start_playback) {
2156
2157 GSList * node;
2158 playlist_t * pl = NULL;
2159 playlist_transfer_t * pt = NULL;
2160 char fullname[MAXLEN];
2161 int type;
2162
2163 for (node = list; node; node = node->next) {
2164
2165 normalize_filename((char *)node->data, fullname);
2166 type = playlist_get_type(fullname);
2167
2168 if (type != PLAYLIST_XML_MULTI) {
2169 /* content goes to the same playlist */
2170 if (pl == NULL) {
2171 pt = playlist_transfer_get(mode, tab_name, start_playback);
2172 pl = pt->pl;
2173 start_playback = 0;
2174 } else {
2175 pt = playlist_transfer_new(pl);
2176 }
2177 }
2178
2179 switch (type) {
2180 case PLAYLIST_INVALID:
2181 /* not a playlist, trying to load as audio file */
2182 pt->list = g_slist_append(NULL, strdup(fullname));
2183 playlist_progress_bar_show(pt->pl);
2184
2185 if (g_file_test(fullname, G_FILE_TEST_IS_DIR)) {
2186 AQUALUNG_THREAD_CREATE(pt->pl->thread_id,
2187 NULL,
2188 add_dir_to_playlist_thread, pt);
2189 } else {
2190 AQUALUNG_THREAD_CREATE(pt->pl->thread_id,
2191 NULL,
2192 add_files_to_playlist_thread, pt);
2193 }
2194
2195 break;
2196
2197 case PLAYLIST_XML_MULTI:
2198 playlist_load_xml_multi(fullname, start_playback);
2199 start_playback = 0;
2200 break;
2201
2202 case PLAYLIST_XML_SINGLE:
2203 playlist_load_xml_single(fullname, pt);
2204 break;
2205
2206 case PLAYLIST_M3U:
2207 playlist_progress_bar_show(pt->pl);
2208 pt->filename = strdup(fullname);
2209 AQUALUNG_THREAD_CREATE(pt->pl->thread_id,
2210 NULL,
2211 playlist_load_m3u_thread,
2212 pt);
2213 break;
2214
2215 case PLAYLIST_PLS:
2216 playlist_progress_bar_show(pt->pl);
2217 pt->filename = strdup(fullname);
2218 AQUALUNG_THREAD_CREATE(pt->pl->thread_id,
2219 NULL,
2220 playlist_load_pls_thread,
2221 pt);
2222 break;
2223 }
2224
2225 free(node->data);
2226 }
2227
2228 g_slist_free(list);
2229 }
2230
2231 void
playlist_load_dialog(int mode)2232 playlist_load_dialog(int mode) {
2233
2234 GSList * files;
2235
2236 files = file_chooser(_("Please specify the file to load the playlist from."),
2237 options.playlist_is_embedded ? main_window : playlist_window,
2238 GTK_FILE_CHOOSER_ACTION_OPEN,
2239 FILE_CHOOSER_FILTER_PLAYLIST,
2240 FALSE,
2241 options.plistdir);
2242 if (files != NULL) {
2243 playlist_load(files, mode, NULL, 0);
2244 }
2245 }
2246
2247 void
plist__load_cb(gpointer data)2248 plist__load_cb(gpointer data) {
2249
2250 playlist_load_dialog(PLAYLIST_LOAD);
2251 }
2252
2253 void
plist__enqueue_cb(gpointer data)2254 plist__enqueue_cb(gpointer data) {
2255
2256 playlist_load_dialog(PLAYLIST_ENQUEUE);
2257 }
2258
2259 void
plist__load_tab_cb(gpointer data)2260 plist__load_tab_cb(gpointer data) {
2261
2262 playlist_load_dialog(PLAYLIST_LOAD_TAB);
2263 }
2264
2265
2266 int
plist_setup_vol_foreach(playlist_t * pl,GtkTreeIter * iter,void * data)2267 plist_setup_vol_foreach(playlist_t * pl, GtkTreeIter * iter, void * data) {
2268
2269 volume_t * vol = (volume_t *)data;
2270 playlist_data_t * pldata;
2271
2272 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &pldata, -1);
2273
2274 if (!httpc_is_url(pldata->file)) {
2275 volume_push(vol, pldata->file, *iter);
2276 }
2277
2278 return 0;
2279 }
2280
2281 void
plist_setup_vol_calc(playlist_t * pl,int type)2282 plist_setup_vol_calc(playlist_t * pl, int type) {
2283
2284 volume_t * vol;
2285
2286 if (!options.rva_is_enabled) {
2287
2288 int ret = message_dialog(_("Warning"),
2289 options.playlist_is_embedded ? main_window : playlist_window,
2290 GTK_MESSAGE_WARNING,
2291 GTK_BUTTONS_YES_NO,
2292 NULL,
2293 _("Playback RVA is currently disabled.\n"
2294 "Do you want to enable it now?"));
2295
2296 if (ret != GTK_RESPONSE_YES) {
2297 return;
2298 }
2299
2300 options.rva_is_enabled = 1;
2301 }
2302
2303 if ((vol = volume_new(pl->store, type)) == NULL) {
2304 return;
2305 }
2306
2307 playlist_foreach_selected(pl, plist_setup_vol_foreach, vol);
2308
2309 volume_start(vol);
2310 }
2311
2312 void
plist__rva_separate_cb(gpointer data)2313 plist__rva_separate_cb(gpointer data) {
2314
2315 playlist_t * pl;
2316
2317 if ((pl = playlist_get_current()) == NULL) {
2318 return;
2319 }
2320
2321 plist_setup_vol_calc(pl, VOLUME_SEPARATE);
2322 }
2323
2324 void
plist__rva_average_cb(gpointer data)2325 plist__rva_average_cb(gpointer data) {
2326
2327 playlist_t * pl;
2328
2329 if ((pl = playlist_get_current()) == NULL) {
2330 return;
2331 }
2332
2333 plist_setup_vol_calc(pl, VOLUME_AVERAGE);
2334 }
2335
2336 int
plist__reread_file_meta_foreach(playlist_t * pl,GtkTreeIter * iter,void * user_data)2337 plist__reread_file_meta_foreach(playlist_t * pl, GtkTreeIter * iter, void * user_data) {
2338
2339 GtkTreeIter parent;
2340 int name_set = 0;
2341
2342 char voladj_str[32];
2343 char duration_str[MAXLEN];
2344 playlist_data_t * data;
2345 playlist_data_t * tmp;
2346
2347 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &data, -1);
2348
2349 if (!g_file_test(data->file, G_FILE_TEST_EXISTS)) {
2350 return 0;
2351 }
2352
2353 if ((tmp = playlist_filemeta_get(data->file)) == NULL) {
2354 fprintf(stderr, "plist__reread_file_meta_foreach(): "
2355 "playlist_filemeta_get() returned NULL\n");
2356 return 0;
2357 }
2358
2359 free_strdup(&data->artist, tmp->artist);
2360 free_strdup(&data->album, tmp->album);
2361 free_strdup(&data->title, tmp->title);
2362 free_strdup(&data->display, tmp->display);
2363
2364 data->voladj = tmp->voladj;
2365 data->duration = tmp->duration;
2366 data->size = tmp->size;
2367
2368 playlist_data_free(tmp);
2369
2370 voladj2str(data->voladj, voladj_str);
2371 time2time_na(data->duration, duration_str);
2372
2373 gtk_tree_store_set(pl->store, iter,
2374 PL_COL_VADJ, voladj_str,
2375 PL_COL_DURA, duration_str,
2376 -1);
2377
2378 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(pl->store), &parent, iter)) {
2379 playlist_data_t * pdata;
2380 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &parent, PL_COL_DATA, &pdata, -1);
2381 if (pdata->artist && pdata->album && data->artist && data->album && data->title &&
2382 !strcmp(pdata->artist, data->artist) && !strcmp(pdata->album, data->album)) {
2383 gtk_tree_store_set(pl->store, iter, PL_COL_NAME, data->title, -1);
2384 name_set = 1;
2385 }
2386 }
2387
2388 if (!name_set) {
2389 char list_str[MAXLEN];
2390 playlist_data_get_display_name(list_str, data);
2391 gtk_tree_store_set(pl->store, iter, PL_COL_NAME, list_str, -1);
2392 }
2393
2394 return 0;
2395 }
2396
2397
2398 void
plist__reread_file_meta_cb(gpointer data)2399 plist__reread_file_meta_cb(gpointer data) {
2400
2401 GtkTreeIter iter;
2402 GtkTreeIter iter_child;
2403 gint i = 0;
2404 gint j = 0;
2405 gint reread = 0;
2406 playlist_t * pl;
2407
2408 if ((pl = playlist_get_current()) == NULL) {
2409 return;
2410 }
2411
2412 playlist_foreach_selected(pl, plist__reread_file_meta_foreach, NULL);
2413
2414 i = 0;
2415 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
2416
2417 if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pl->store), &iter)) {
2418
2419 reread = 0;
2420
2421 if (gtk_tree_selection_iter_is_selected(pl->select, &iter)) {
2422 reread = 1;
2423 } else {
2424 j = 0;
2425 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store),
2426 &iter_child, &iter, j++)) {
2427 if (gtk_tree_selection_iter_is_selected(pl->select,
2428 &iter_child)) {
2429 reread = 1;
2430 break;
2431 }
2432 }
2433 }
2434
2435 if (reread) {
2436 recalc_album_node(pl, &iter);
2437 }
2438 }
2439 }
2440
2441 playlist_content_changed(pl);
2442 }
2443
2444 void
playlist_update_metadata(void)2445 playlist_update_metadata(void) {
2446
2447 GList * node = NULL;
2448
2449 for (node = playlists; node; node = node->next) {
2450 playlist_t * pl = (playlist_t *)node->data;
2451 GtkTreeIter iter;
2452 gint i = 0;
2453 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
2454 plist__reread_file_meta_foreach(pl, &iter, NULL);
2455 }
2456 playlist_content_changed(pl);
2457 }
2458 }
2459
2460 #ifdef HAVE_TRANSCODING
2461 int
plist__export_foreach(playlist_t * pl,GtkTreeIter * iter,void * data)2462 plist__export_foreach(playlist_t * pl, GtkTreeIter * iter, void * data) {
2463
2464 export_t * export = (export_t *)data;
2465 playlist_data_t * pldata;
2466 file_decoder_t * fdec;
2467
2468 char artist[MAXLEN];
2469 char album[MAXLEN];
2470 char title[MAXLEN];
2471 int year = 0;
2472 int no = 0;
2473
2474 artist[0] = '\0';
2475 album[0] = '\0';
2476 title[0] = '\0';
2477
2478 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &pldata, -1);
2479
2480 if (!g_file_test(pldata->file, G_FILE_TEST_EXISTS)) {
2481 return 0;
2482 }
2483
2484 fdec = file_decoder_new();
2485 if (fdec == NULL) {
2486 fprintf(stderr, "plist__export_foreach: file_decoder_new() failed\n");
2487 return 0;
2488 }
2489
2490 if (file_decoder_open(fdec, pldata->file) == 0) {
2491
2492 char * tmp;
2493
2494 if (pldata->artist) {
2495 strcpy(artist, pldata->artist);
2496 } else if (metadata_get_artist(fdec->meta, &tmp)) {
2497 strncpy(artist, tmp, MAXLEN-1);
2498 }
2499
2500 if (pldata->album) {
2501 strcpy(album, pldata->album);
2502 } else if (metadata_get_album(fdec->meta, &tmp)) {
2503 strncpy(album, tmp, MAXLEN-1);
2504 }
2505
2506 if (pldata->title) {
2507 strcpy(title, pldata->title);
2508 } else if (metadata_get_title(fdec->meta, &tmp)) {
2509 strncpy(title, tmp, MAXLEN-1);
2510 }
2511
2512 if (metadata_get_date(fdec->meta, &tmp)) {
2513 year = atoi(tmp);
2514 }
2515
2516 if (!metadata_get_tracknum(fdec->meta, &no)) {
2517 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl->store), iter);
2518 int depth = gtk_tree_path_get_depth(path);
2519 gint * indices = gtk_tree_path_get_indices(path);
2520 no = indices[depth-1] + 1;
2521 gtk_tree_path_free(path);
2522 }
2523
2524 file_decoder_close(fdec);
2525 }
2526 file_decoder_delete(fdec);
2527
2528 export_append_item(export, pldata->file, artist, album, title, year, no);
2529
2530 return 0;
2531 }
2532
2533 void
plist__export_cb(gpointer data)2534 plist__export_cb(gpointer data) {
2535
2536 export_t * export;
2537 playlist_t * pl;
2538
2539 if ((pl = playlist_get_current()) == NULL) {
2540 return;
2541 }
2542
2543 if ((export = export_new()) == NULL) {
2544 return;
2545 }
2546
2547 playlist_foreach_selected(pl, plist__export_foreach, export);
2548
2549 export_start(export);
2550 }
2551 #endif /* HAVE_TRANSCODING */
2552
2553 #ifdef HAVE_IFP
2554 void
plist__send_songs_to_iriver_cb(gpointer data)2555 plist__send_songs_to_iriver_cb(gpointer data) {
2556
2557 aifp_transfer_files(UPLOAD_MODE);
2558
2559 }
2560 #endif /* HAVE_IFP */
2561
2562 void
plist__fileinfo_cb(gpointer user_data)2563 plist__fileinfo_cb(gpointer user_data) {
2564
2565 playlist_t * pl = playlist_get_current();
2566 playlist_data_t * pldata;
2567 GtkTreeIter iter;
2568
2569 if (!pl) return;
2570 if (fileinfo_iter) {
2571 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), fileinfo_iter, PL_COL_DATA, &pldata, -1);
2572 iter = *fileinfo_iter;
2573 gtk_tree_iter_free(fileinfo_iter);
2574 fileinfo_iter = NULL; // mark it as consumed
2575 } else {
2576 GtkTreePath * p = playlist_get_playing_path(pl);
2577 if (p == NULL) return;
2578 gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
2579 gtk_tree_path_free(p);
2580 }
2581 show_file_info(GTK_TREE_MODEL(pl->store), iter, playlist_model_func, 0,
2582 FALSE, IS_PL_COVER(pldata));
2583 }
2584
2585 void
plist__search_cb(gpointer data)2586 plist__search_cb(gpointer data) {
2587
2588 search_playlist_dialog();
2589 }
2590
2591 void
plist__roll_cb(gpointer data)2592 plist__roll_cb(gpointer data) {
2593
2594 playlist_t * pl;
2595
2596 if ((pl = playlist_get_current()) == NULL) {
2597 return;
2598 }
2599
2600 show_active_position_in_playlist_toggle(pl);
2601 }
2602
2603
2604 static gboolean
add_cb(GtkWidget * widget,GdkEvent * event)2605 add_cb(GtkWidget * widget, GdkEvent * event) {
2606
2607 if (event->type == GDK_BUTTON_PRESS) {
2608 GdkEventButton * bevent = (GdkEventButton *) event;
2609
2610 if (bevent->button == 3) {
2611
2612 gtk_menu_popup(GTK_MENU(add_menu), NULL, NULL, NULL, NULL,
2613 bevent->button, bevent->time);
2614
2615 return TRUE;
2616 }
2617 return FALSE;
2618 }
2619 return FALSE;
2620 }
2621
2622
2623 static gboolean
sel_cb(GtkWidget * widget,GdkEvent * event)2624 sel_cb(GtkWidget * widget, GdkEvent * event) {
2625
2626 if (event->type == GDK_BUTTON_PRESS) {
2627 GdkEventButton * bevent = (GdkEventButton *) event;
2628
2629 if (bevent->button == 3) {
2630 gtk_menu_popup(GTK_MENU(sel_menu), NULL, NULL, NULL, NULL,
2631 bevent->button, bevent->time);
2632 return TRUE;
2633 }
2634 return FALSE;
2635 }
2636 return FALSE;
2637 }
2638
2639
2640 static gboolean
rem_cb(GtkWidget * widget,GdkEvent * event)2641 rem_cb(GtkWidget * widget, GdkEvent * event) {
2642
2643 if (event->type == GDK_BUTTON_PRESS) {
2644 GdkEventButton * bevent = (GdkEventButton *) event;
2645
2646 if (bevent->button == 3) {
2647 gtk_menu_popup(GTK_MENU(rem_menu), NULL, NULL, NULL, NULL,
2648 bevent->button, bevent->time);
2649 return TRUE;
2650 }
2651 return FALSE;
2652 }
2653 return FALSE;
2654 }
2655
2656
2657 playlist_data_t *
playlist_filemeta_get(char * filename)2658 playlist_filemeta_get(char * filename) {
2659
2660 playlist_data_t * data;
2661 file_decoder_t * fdec;
2662 struct stat statbuf;
2663 char * tmp;
2664
2665 if ((data = playlist_data_new()) == NULL) {
2666 return NULL;
2667 }
2668
2669 fdec = file_decoder_new();
2670 if (fdec == NULL) {
2671 fprintf(stderr, "playlist_filemeta_get: file_decoder_new() failed\n");
2672 playlist_data_free(data);
2673 return NULL;
2674 }
2675 if (file_decoder_open(fdec, filename) != 0) {
2676 file_decoder_delete(fdec);
2677 playlist_data_free(data);
2678 return NULL;
2679 }
2680
2681 if (g_stat(data->file, &statbuf) != -1) {
2682 data->size = statbuf.st_size;
2683 }
2684
2685 data->duration = (float)fdec->fileinfo.total_samples / fdec->fileinfo.sample_rate;
2686
2687 if (options.rva_is_enabled) {
2688 if (!metadata_get_rva(fdec->meta, &data->voladj)) {
2689 data->voladj = options.rva_no_rva_voladj;
2690 }
2691 } else {
2692 data->voladj = 0.0f;
2693 }
2694
2695 if (metadata_get_artist(fdec->meta, &tmp) && !is_all_wspace(tmp)) {
2696 free_strdup(&data->artist, tmp);
2697 }
2698 if (metadata_get_album(fdec->meta, &tmp) && !is_all_wspace(tmp)) {
2699 free_strdup(&data->album, tmp);
2700 }
2701 if (metadata_get_title(fdec->meta, &tmp) && !is_all_wspace(tmp)) {
2702 free_strdup(&data->title, tmp);
2703 }
2704
2705 data->display = extended_title_format(fdec);
2706
2707 file_decoder_close(fdec);
2708 file_decoder_delete(fdec);
2709
2710 data->file = strdup(filename);
2711
2712 return data;
2713 }
2714
2715
2716 void
add__files_cb(gpointer data)2717 add__files_cb(gpointer data) {
2718
2719 add_files(NULL, NULL);
2720 }
2721
2722
2723 void
add__dir_cb(gpointer data)2724 add__dir_cb(gpointer data) {
2725
2726 add_directory(NULL, NULL);
2727 }
2728
2729
2730 void
add__url_cb(gpointer data)2731 add__url_cb(gpointer data) {
2732
2733 add_url(NULL, NULL);
2734 }
2735
2736
2737 void
select_all(GtkWidget * widget,gpointer data)2738 select_all(GtkWidget * widget, gpointer data) {
2739
2740 playlist_t * pl = playlist_get_current();
2741
2742 if (pl != NULL) {
2743 gtk_tree_selection_select_all(pl->select);
2744 }
2745 }
2746
2747
2748 void
sel__all_cb(gpointer data)2749 sel__all_cb(gpointer data) {
2750
2751 select_all(NULL, data);
2752 }
2753
2754
2755 void
sel__none_cb(gpointer data)2756 sel__none_cb(gpointer data) {
2757
2758 playlist_t * pl;
2759
2760 if ((pl = playlist_get_current()) != NULL) {
2761 gtk_tree_selection_unselect_all(pl->select);
2762 }
2763 }
2764
2765
2766 void
sel__inv_foreach(GtkTreePath * path,gpointer data)2767 sel__inv_foreach(GtkTreePath * path, gpointer data) {
2768
2769 gtk_tree_selection_unselect_path((GtkTreeSelection *)data, path);
2770 gtk_tree_path_free(path);
2771 }
2772
2773
2774 void
sel__inv_cb(gpointer data)2775 sel__inv_cb(gpointer data) {
2776
2777
2778 GList * list = NULL;
2779 playlist_t * pl = NULL;
2780
2781 if ((pl = playlist_get_current()) == NULL) {
2782 return;
2783 }
2784
2785 list = gtk_tree_selection_get_selected_rows(pl->select, NULL);
2786
2787 g_signal_handlers_block_by_func(G_OBJECT(pl->select), playlist_selection_changed_cb, pl);
2788
2789 gtk_tree_selection_select_all(pl->select);
2790 g_list_foreach(list, (GFunc)sel__inv_foreach, pl->select);
2791 g_list_free(list);
2792
2793 g_signal_handlers_unblock_by_func(G_OBJECT(pl->select), playlist_selection_changed_cb, pl);
2794
2795 playlist_selection_changed(pl);
2796 }
2797
2798
2799 void
rem_all(playlist_t * pl)2800 rem_all(playlist_t * pl) {
2801
2802 g_signal_handlers_block_by_func(G_OBJECT(pl->select), playlist_selection_changed_cb, pl);
2803
2804 playlist_clear(pl->store);
2805
2806 g_signal_handlers_unblock_by_func(G_OBJECT(pl->select), playlist_selection_changed_cb, pl);
2807
2808 if (pl == playlist_get_current()) {
2809 playlist_selection_changed(pl);
2810 playlist_content_changed(pl);
2811 }
2812 }
2813
2814 void
rem__all_cb(gpointer data)2815 rem__all_cb(gpointer data) {
2816
2817 playlist_t * pl;
2818
2819 if ((pl = playlist_get_current()) == NULL) {
2820 return;
2821 }
2822
2823 rem_all(pl);
2824 }
2825
2826 void
rem__sel_cb(gpointer data)2827 rem__sel_cb(gpointer data) {
2828
2829 GtkTreeModel * model;
2830 GtkTreeIter iter;
2831 gint i = 0;
2832 playlist_t * pl;
2833 GtkTreePath * path = NULL;
2834
2835
2836 if (data != NULL) {
2837 pl = (playlist_t *)data;
2838 } else if ((pl = playlist_get_current()) == NULL) {
2839 return;
2840 }
2841
2842 g_signal_handlers_block_by_func(G_OBJECT(pl->select), playlist_selection_changed_cb, pl);
2843
2844 model = GTK_TREE_MODEL(pl->store);
2845
2846 while (gtk_tree_model_iter_nth_child(model, &iter, NULL, i++)) {
2847
2848 if (gtk_tree_selection_iter_is_selected(pl->select, &iter)) {
2849 if (path == NULL) {
2850 path = gtk_tree_model_get_path(model, &iter);
2851 }
2852 playlist_remove_track(pl->store, &iter);
2853 i--;
2854 continue;
2855 }
2856
2857 if (gtk_tree_model_iter_n_children(model, &iter) == 0) {
2858 continue;
2859 }
2860
2861 if (all_tracks_selected(pl, &iter)) {
2862 if (path == NULL) {
2863 path = gtk_tree_model_get_path(model, &iter);
2864 }
2865 playlist_remove_track(pl->store, &iter);
2866 i--;
2867 } else {
2868 int j = 0;
2869 int recalc = 0;
2870 GtkTreeIter iter_child;
2871
2872 while (gtk_tree_model_iter_nth_child(model, &iter_child, &iter, j++)) {
2873 if (gtk_tree_selection_iter_is_selected(pl->select, &iter_child)) {
2874 if (path == NULL) {
2875 path = gtk_tree_model_get_path(model, &iter_child);
2876 }
2877 playlist_remove_track(pl->store, &iter_child);
2878 recalc = 1;
2879 j--;
2880 }
2881 }
2882
2883 if (recalc) {
2884 recalc_album_node(pl, &iter);
2885 }
2886 }
2887 }
2888
2889 if (path != NULL) {
2890 if (gtk_tree_model_get_iter(model, &iter, path)) {
2891 set_cursor_in_playlist(pl, &iter, TRUE);
2892 } else {
2893 if (gtk_tree_path_get_depth(path) == 1) {
2894 int n = gtk_tree_model_iter_n_children(model, NULL);
2895 if (n > 0) {
2896 gtk_tree_model_iter_nth_child(model, &iter, NULL, n-1);
2897 set_cursor_in_playlist(pl, &iter, TRUE);
2898 }
2899 } else {
2900 GtkTreeIter iter_child;
2901
2902 gtk_tree_path_up(path);
2903 gtk_tree_model_get_iter(model, &iter, path);
2904
2905 gtk_tree_model_iter_nth_child(model, &iter_child, &iter,
2906 gtk_tree_model_iter_n_children(model, &iter)-1);
2907 set_cursor_in_playlist(pl, &iter_child, TRUE);
2908 }
2909 }
2910
2911 gtk_tree_path_free(path);
2912 }
2913
2914 g_signal_handlers_unblock_by_func(G_OBJECT(pl->select), playlist_selection_changed_cb, pl);
2915
2916
2917 playlist_content_changed(pl);
2918 playlist_selection_changed(pl);
2919 }
2920
2921
2922 int
rem__dead_skip(char * filename)2923 rem__dead_skip(char * filename) {
2924
2925 return (filename != NULL &&
2926 !g_str_has_prefix(filename, "CDDA ") &&
2927 !httpc_is_url(filename) &&
2928 (g_file_test(filename, G_FILE_TEST_IS_REGULAR) == FALSE));
2929 }
2930
2931 void
rem__dead_cb(gpointer user_data)2932 rem__dead_cb(gpointer user_data) {
2933
2934 GtkTreeIter iter;
2935 gint i = 0;
2936 playlist_t * pl;
2937 playlist_data_t * data;
2938
2939 if ((pl = playlist_get_current()) == NULL) {
2940 return;
2941 }
2942
2943 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
2944
2945 GtkTreeIter iter_child;
2946 gint j = 0;
2947
2948
2949 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
2950
2951 gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter);
2952
2953 if (n == 0 && rem__dead_skip(data->file)) {
2954 playlist_remove_track(pl->store, &iter);
2955 --i;
2956 continue;
2957 }
2958
2959 if (n) {
2960 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_child, &iter, j++)) {
2961 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter_child, PL_COL_DATA, &data, -1);
2962
2963 if (rem__dead_skip(data->file)) {
2964 playlist_remove_track(pl->store, &iter_child);
2965 --j;
2966 }
2967 }
2968
2969 /* remove album node if empty */
2970 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter) == 0) {
2971 playlist_remove_track(pl->store, &iter);
2972 --i;
2973 } else {
2974 recalc_album_node(pl, &iter);
2975 }
2976 }
2977
2978 }
2979
2980 playlist_content_changed(pl);
2981 }
2982
2983 void
remove_sel(GtkWidget * widget,gpointer data)2984 remove_sel(GtkWidget * widget, gpointer data) {
2985
2986 rem__sel_cb(data);
2987 }
2988
2989
2990 /* playlist item is selected -> keep, else -> remove
2991 * ret: 0 if kept, 1 if removed track
2992 */
2993 int
cut_track_item(playlist_t * pl,GtkTreeIter * piter)2994 cut_track_item(playlist_t * pl, GtkTreeIter * piter) {
2995
2996 if (!gtk_tree_selection_iter_is_selected(pl->select, piter)) {
2997 playlist_remove_track(pl->store, piter);
2998 return 1;
2999 }
3000 return 0;
3001 }
3002
3003
3004 /* ret: 1 if at least one of album node's children are selected; else 0 */
3005 int
any_tracks_selected(playlist_t * pl,GtkTreeIter * piter)3006 any_tracks_selected(playlist_t * pl, GtkTreeIter * piter) {
3007
3008 gint j = 0;
3009 GtkTreeIter iter_child;
3010
3011 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_child, piter, j++)) {
3012 if (gtk_tree_selection_iter_is_selected(pl->select, &iter_child)) {
3013 return 1;
3014 }
3015 }
3016 return 0;
3017 }
3018
3019
3020 /* cut selected callback */
3021 void
cut__sel_cb(gpointer data)3022 cut__sel_cb(gpointer data) {
3023
3024 GtkTreeIter iter;
3025 gint i = 0;
3026 playlist_t * pl;
3027
3028 if ((pl = playlist_get_current()) == NULL) {
3029 return;
3030 }
3031
3032 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
3033
3034 gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter);
3035
3036 if (n) { /* album node */
3037 if (any_tracks_selected(pl, &iter)) {
3038 gint j = 0;
3039 GtkTreeIter iter_child;
3040 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store),
3041 &iter_child, &iter, j++)) {
3042 j -= cut_track_item(pl, &iter_child);
3043 }
3044
3045 recalc_album_node(pl, &iter);
3046 } else {
3047 i -= cut_track_item(pl, &iter);
3048 }
3049 } else { /* track node */
3050 i -= cut_track_item(pl, &iter);
3051 }
3052 }
3053
3054 gtk_tree_selection_unselect_all(pl->select);
3055 playlist_content_changed(pl);
3056 }
3057
3058
3059 void
playlist_reorder_columns_foreach(gpointer data,gpointer user_data)3060 playlist_reorder_columns_foreach(gpointer data, gpointer user_data) {
3061
3062 playlist_t * pl = (playlist_t *)data;
3063 int * order = (int *)user_data;
3064 GtkTreeViewColumn * cols[3];
3065 int i;
3066
3067 cols[0] = pl->track_column;
3068 cols[1] = pl->rva_column;
3069 cols[2] = pl->length_column;
3070
3071 for (i = 2; i >= 0; i--) {
3072 gtk_tree_view_move_column_after(GTK_TREE_VIEW(pl->view),
3073 cols[order[i]], NULL);
3074 }
3075
3076 gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(pl->rva_column),
3077 options.show_rva_in_playlist);
3078 gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(pl->length_column),
3079 options.show_length_in_playlist);
3080 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(pl->view), options.enable_pl_rules_hint);
3081 }
3082
3083 void
playlist_reorder_columns_all(int * order)3084 playlist_reorder_columns_all(int * order) {
3085
3086 g_list_foreach(playlists, playlist_reorder_columns_foreach, order);
3087
3088 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) == 1) {
3089 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(playlist_notebook),
3090 options.playlist_always_show_tabs);
3091 }
3092 }
3093
3094 void
playlist_stats_set_busy()3095 playlist_stats_set_busy() {
3096
3097 gtk_label_set_text(GTK_LABEL(statusbar_total), _("counting..."));
3098 }
3099
3100 void
playlist_child_stats(playlist_t * pl,GtkTreeIter * iter,int * ntrack,float * length,double * size,int selected)3101 playlist_child_stats(playlist_t * pl, GtkTreeIter * iter,
3102 int * ntrack, float * length, double * size, int selected) {
3103
3104 int j = 0;
3105 GtkTreeIter iter_child;
3106 playlist_data_t * data;
3107
3108 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_child, iter, j++)) {
3109
3110 if (!selected || gtk_tree_selection_iter_is_selected(pl->select, &iter_child)) {
3111
3112 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter_child, PL_COL_DATA, &data, -1);
3113
3114 if (data->size == 0) {
3115 struct stat statbuf;
3116 if (g_stat(data->file, &statbuf) != -1) {
3117 data->size = statbuf.st_size;
3118 }
3119 }
3120
3121 *size += data->size / 1024.0;
3122 *length += data->duration;
3123 (*ntrack)++;
3124 }
3125 }
3126 }
3127
3128
3129 /* if selected == true -> stats for selected tracks; else: all tracks */
3130 void
playlist_stats(playlist_t * pl,int selected)3131 playlist_stats(playlist_t * pl, int selected) {
3132
3133 GtkTreeIter iter;
3134 int i = 0;
3135
3136 int ntrack = 0;
3137 float length = 0;
3138 double size = 0;
3139
3140 char str[MAXLEN];
3141 char length_str[MAXLEN];
3142 char tmp[MAXLEN];
3143
3144 playlist_data_t * data;
3145
3146
3147 if (!options.enable_playlist_statusbar) {
3148 return;
3149 }
3150
3151 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
3152
3153 gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter);
3154 if (n > 0) {
3155 if (gtk_tree_selection_iter_is_selected(pl->select, &iter)) {
3156 playlist_child_stats(pl, &iter, &ntrack, &length, &size, 0/*false*/);
3157 } else {
3158 playlist_child_stats(pl, &iter, &ntrack, &length, &size, selected);
3159 }
3160 } else {
3161 if (!selected || gtk_tree_selection_iter_is_selected(pl->select, &iter)) {
3162
3163 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
3164
3165 if (data->size == 0) {
3166 struct stat statbuf;
3167 if (g_stat(data->file, &statbuf) != -1) {
3168 data->size = statbuf.st_size;
3169 }
3170 }
3171
3172 size += data->size / 1024.0;
3173 length += data->duration;
3174 ntrack++;
3175 }
3176 }
3177 }
3178
3179 sprintf(str, " %d %s ", ntrack, (ntrack == 1) ? _("track") : _("tracks"));
3180
3181 if (length > 0.0f || ntrack == 0) {
3182 time2time(length, length_str);
3183 sprintf(tmp, " [%s] ", length_str);
3184 strcat(str, tmp);
3185 }
3186
3187 if (options.pl_statusbar_show_size) {
3188 if (size > 1024 * 1024) {
3189 sprintf(tmp, " (%.1f GB) ", size / (1024 * 1024));
3190 strcat(str, tmp);
3191 } else if (size > (1 << 10)){
3192 sprintf(tmp, " (%.1f MB) ", size / 1024);
3193 strcat(str, tmp);
3194 } else if (size > 0 || ntrack == 0) {
3195 sprintf(tmp, " (%.1f KB) ", size);
3196 strcat(str, tmp);
3197 }
3198 }
3199
3200 if (selected) {
3201 gtk_label_set_text(GTK_LABEL(statusbar_selected), str);
3202 } else {
3203 gtk_label_set_text(GTK_LABEL(statusbar_total), str);
3204 }
3205 }
3206
3207
3208 void
recalc_album_node(playlist_t * pl,GtkTreeIter * iter)3209 recalc_album_node(playlist_t * pl, GtkTreeIter * iter) {
3210
3211 gint ntrack = 0;
3212 gfloat length = 0;
3213 double size = 0.0;
3214 gchar time[MAXLEN];
3215 playlist_data_t * data;
3216
3217 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &data, -1);
3218
3219 playlist_child_stats(pl, iter, &ntrack, &length, &size, 0/*false*/);
3220
3221 time2time(length, time);
3222 gtk_tree_store_set(pl->store, iter, PL_COL_DURA, time, -1);
3223 data->duration = length;
3224
3225 if (IS_PL_ACTIVE(data)) {
3226 unmark_track(pl, iter);
3227 mark_track(pl, iter);
3228 }
3229 }
3230
3231
3232 void
playlist_selection_changed(playlist_t * pl)3233 playlist_selection_changed(playlist_t * pl) {
3234
3235 playlist_stats(pl, 1/* true */);
3236 }
3237
3238
3239 void
playlist_content_changed(playlist_t * pl)3240 playlist_content_changed(playlist_t * pl) {
3241
3242 if (pl->progbar_semaphore == 0 && pl->ms_semaphore == 0) {
3243 playlist_stats(pl, 0/*false*/);
3244 playlist_dirty = 1;
3245 } else {
3246 playlist_stats_set_busy();
3247 }
3248 }
3249
3250 void
playlist_selection_changed_cb(GtkTreeSelection * select,gpointer data)3251 playlist_selection_changed_cb(GtkTreeSelection * select, gpointer data) {
3252
3253 playlist_t * pl = (playlist_t *)data;
3254
3255 if (pl == playlist_get_current()) {
3256 playlist_selection_changed(pl);
3257 }
3258 }
3259
3260 void
playlist_drag_data_get(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * data,guint info,guint time,gpointer user_data)3261 playlist_drag_data_get(GtkWidget * widget, GdkDragContext * drag_context,
3262 GtkSelectionData * data, guint info, guint time, gpointer user_data) {
3263
3264 char tmp[32];
3265 sprintf(tmp, "%p", user_data);
3266 gtk_selection_data_set(data, gtk_selection_data_get_target(data), 8,
3267 (guchar *)tmp, strlen(tmp));
3268 }
3269
3270 void
playlist_perform_drag(playlist_t * spl,GtkTreeIter * siter,GtkTreePath * spath,playlist_t * tpl,GtkTreeIter * titer,GtkTreePath * tpath)3271 playlist_perform_drag(playlist_t * spl, GtkTreeIter * siter, GtkTreePath * spath,
3272 playlist_t * tpl, GtkTreeIter * titer, GtkTreePath * tpath) {
3273
3274 GtkTreeIter sparent;
3275 GtkTreeIter tparent;
3276 int recalc_sparent = 0;
3277 int recalc_tparent = 0;
3278 gint cmp = 2/*append*/;
3279
3280
3281 if (tpath != NULL) {
3282 cmp = gtk_tree_path_compare(spath, tpath);
3283 }
3284
3285 if (spl == tpl && cmp == 0) {
3286 return;
3287 }
3288
3289 if (titer && tpath &&
3290 gtk_tree_model_iter_has_child(GTK_TREE_MODEL(spl->store), siter) &&
3291 !gtk_tree_model_iter_has_child(GTK_TREE_MODEL(tpl->store), titer) &&
3292 gtk_tree_path_get_depth(tpath) == 2) {
3293 return;
3294 }
3295
3296 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(spl->store), &sparent, siter)) {
3297 recalc_sparent = 1;
3298 }
3299
3300 if (titer && gtk_tree_model_iter_parent(GTK_TREE_MODEL(tpl->store), &tparent, titer)) {
3301 recalc_tparent = 1;
3302 }
3303
3304 playlist_node_deep_copy(spl->store, siter, tpl->store, titer, titer ? (cmp == 1) : 2);
3305
3306 gtk_tree_store_remove(spl->store, siter);
3307
3308 if (tpath) {
3309 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW (tpl->view), tpath, NULL, FALSE, 0, 0);
3310 }
3311
3312 if (recalc_sparent) {
3313 if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(spl->store), &sparent)) {
3314 recalc_album_node(spl, &sparent);
3315 unmark_track(spl, &sparent);
3316 mark_track(spl, &sparent);
3317 } else {
3318 gtk_tree_store_remove(spl->store, &sparent);
3319 }
3320 }
3321
3322 if (recalc_tparent) {
3323 recalc_album_node(tpl, &tparent);
3324 unmark_track(tpl, &tparent);
3325 mark_track(tpl, &tparent);
3326 }
3327
3328 playlist_content_changed(tpl);
3329 }
3330
3331
3332 void
playlist_remove_scroll_tags()3333 playlist_remove_scroll_tags() {
3334
3335 if (playlist_scroll_up_tag > 0) {
3336 g_source_remove(playlist_scroll_up_tag);
3337 playlist_scroll_up_tag = -1;
3338 }
3339 if (playlist_scroll_dn_tag > 0) {
3340 g_source_remove(playlist_scroll_dn_tag);
3341 playlist_scroll_dn_tag = -1;
3342 }
3343 }
3344
3345 gint
playlist_drag_data_received(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time,gpointer user_data)3346 playlist_drag_data_received(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y,
3347 GtkSelectionData * data, guint info, guint time, gpointer user_data) {
3348
3349 playlist_t * tpl = (playlist_t *)user_data;
3350
3351 if (info == 0) { /* drag and drop inside or between playlists */
3352
3353 GtkTreeIter siter;
3354 GtkTreeIter titer;
3355 GtkTreePath * spath = NULL;
3356 GtkTreePath * tpath = NULL;
3357 playlist_t * spl = NULL;
3358
3359
3360 sscanf((char *)gtk_selection_data_get_data(data), "%p", &spl);
3361 gtk_tree_selection_set_mode(spl->select, GTK_SELECTION_SINGLE);
3362
3363 if (gtk_tree_selection_get_selected(spl->select, NULL, &siter)) {
3364
3365 spath = gtk_tree_model_get_path(GTK_TREE_MODEL(spl->store), &siter);
3366
3367 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tpl->view),
3368 x, y, &tpath, NULL, NULL, NULL)) {
3369
3370 gtk_tree_model_get_iter(GTK_TREE_MODEL(tpl->store), &titer, tpath);
3371 playlist_perform_drag(spl, &siter, spath, tpl, &titer, tpath);
3372 } else {
3373 playlist_perform_drag(spl, &siter, spath, tpl, NULL, NULL);
3374 }
3375 }
3376
3377 if (spath) {
3378 gtk_tree_path_free(spath);
3379 }
3380
3381 if (tpath) {
3382 gtk_tree_path_free(tpath);
3383 }
3384
3385 gtk_tree_selection_set_mode(spl->select, GTK_SELECTION_MULTIPLE);
3386
3387 } else if (info == 1) { /* drag and drop from music store to playlist */
3388
3389 GtkTreePath * path = NULL;
3390 GtkTreeIter * piter = NULL;
3391 GtkTreeIter ms_iter;
3392 GtkTreeIter pl_iter;
3393 GtkTreeIter parent;
3394 GtkTreeModel * model;
3395
3396 if (!gtk_tree_selection_get_selected(music_select, &model, &ms_iter)) {
3397 return FALSE;
3398 }
3399
3400 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tpl->view),
3401 x, y, &path, NULL, NULL, NULL)) {
3402
3403 if (!music_store_iter_is_track(&ms_iter)) {
3404 while (gtk_tree_path_get_depth(path) > 1) {
3405 gtk_tree_path_up(path);
3406 }
3407 }
3408
3409 gtk_tree_model_get_iter(GTK_TREE_MODEL(tpl->store), &pl_iter, path);
3410 piter = &pl_iter;
3411 }
3412
3413 music_store_iter_addlist_defmode(&ms_iter, piter, 0/*new_tab*/);
3414
3415 if (piter && gtk_tree_model_iter_parent(GTK_TREE_MODEL(tpl->store), &parent, piter) &&
3416 music_store_iter_is_track(&ms_iter)) {
3417 recalc_album_node(tpl, &parent);
3418 unmark_track(tpl, &parent);
3419 mark_track(tpl, &parent);
3420 }
3421
3422 if (path) {
3423 gtk_tree_path_free(path);
3424 }
3425
3426 } else if (info == 2) { /* drag and drop from external app */
3427
3428 GSList * list = NULL;
3429 gchar ** uri_list;
3430 gchar * str = NULL;
3431 int i;
3432 char file[MAXLEN];
3433
3434 uri_list = g_strsplit_set((gchar *)gtk_selection_data_get_data(data),
3435 "\r\n", 0);
3436
3437 for (i = 0; uri_list[i]; i++) {
3438
3439 if (*(uri_list[i]) == '\0') {
3440 continue;
3441 }
3442
3443 if ((str = g_filename_from_uri(uri_list[i], NULL, NULL)) != NULL) {
3444 strncpy(file, str, MAXLEN-1);
3445 g_free(str);
3446 } else {
3447 int off = 0;
3448
3449 if (strstr(uri_list[i], "file:") == uri_list[i] ||
3450 strstr(uri_list[i], "FILE:") == uri_list[i]) {
3451 off = 5;
3452 }
3453
3454 while (uri_list[i][off] == '/' && uri_list[i][off+1] == '/') {
3455 off++;
3456 }
3457
3458 strncpy(file, uri_list[i] + off, MAXLEN-1);
3459 }
3460
3461 if ((str = g_filename_from_utf8(file, -1, NULL, NULL, NULL)) != NULL) {
3462 strncpy(file, str, MAXLEN-1);
3463 g_free(str);
3464 }
3465
3466 if (g_file_test(file, G_FILE_TEST_EXISTS) || httpc_is_url(file)) {
3467 list = g_slist_append(list, strdup(file));
3468 }
3469 }
3470
3471 if (list != NULL) {
3472 playlist_load(list, PLAYLIST_ENQUEUE, NULL, 0);
3473 }
3474
3475 g_strfreev(uri_list);
3476
3477 playlist_remove_scroll_tags();
3478 }
3479
3480 return FALSE;
3481 }
3482
3483 gint
playlist_scroll_up(gpointer data)3484 playlist_scroll_up(gpointer data) {
3485
3486 playlist_t * pl = (playlist_t *)data;
3487
3488 gboolean dummy;
3489 GtkRange * range = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(pl->scroll)));
3490
3491 g_signal_emit_by_name(G_OBJECT(range), "move-slider", GTK_SCROLL_STEP_UP, &dummy);
3492
3493 return TRUE;
3494 }
3495
3496 gint
playlist_scroll_dn(gpointer data)3497 playlist_scroll_dn(gpointer data) {
3498
3499 playlist_t * pl = (playlist_t *)data;
3500
3501 gboolean dummy;
3502 GtkRange * range = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(pl->scroll)));
3503
3504 g_signal_emit_by_name(G_OBJECT(range), "move-slider", GTK_SCROLL_STEP_DOWN, &dummy);
3505
3506 return TRUE;
3507 }
3508
3509
3510 void
playlist_drag_leave(GtkWidget * widget,GdkDragContext * drag_context,guint time,gpointer data)3511 playlist_drag_leave(GtkWidget * widget, GdkDragContext * drag_context,
3512 guint time, gpointer data) {
3513
3514 playlist_remove_scroll_tags();
3515 }
3516
3517
3518 gboolean
playlist_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,gpointer data)3519 playlist_drag_motion(GtkWidget * widget, GdkDragContext * context,
3520 gint x, gint y, guint time, gpointer data) {
3521
3522 if (y < 30) {
3523 if (playlist_scroll_up_tag == -1) {
3524 playlist_scroll_up_tag = aqualung_timeout_add(100, playlist_scroll_up, data);
3525 }
3526 } else {
3527 if (playlist_scroll_up_tag > 0) {
3528 g_source_remove(playlist_scroll_up_tag);
3529 playlist_scroll_up_tag = -1;
3530 }
3531 }
3532
3533 if (y > widget->allocation.height - 30) {
3534 if (playlist_scroll_dn_tag == -1) {
3535 playlist_scroll_dn_tag = aqualung_timeout_add(100, playlist_scroll_dn, data);
3536 }
3537 } else {
3538 if (playlist_scroll_dn_tag > 0) {
3539 g_source_remove(playlist_scroll_dn_tag);
3540 playlist_scroll_dn_tag = -1;
3541 }
3542 }
3543
3544 return TRUE;
3545 }
3546
3547 void
playlist_drag_end(GtkWidget * widget,GdkDragContext * drag_context,gpointer data)3548 playlist_drag_end(GtkWidget * widget, GdkDragContext * drag_context, gpointer data) {
3549
3550 playlist_remove_scroll_tags();
3551 }
3552
3553
3554 void
playlist_rename(playlist_t * pl,char * name)3555 playlist_rename(playlist_t * pl, char * name) {
3556
3557 if (name != NULL) {
3558 strncpy(pl->name, name, MAXLEN-1);
3559 pl->name_set = 1;
3560 playlist_set_markup(pl);
3561 }
3562 }
3563
3564 void
playlist_notebook_prev_page(void)3565 playlist_notebook_prev_page(void) {
3566
3567 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(playlist_notebook)) == 0) {
3568 gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), -1);
3569 } else {
3570 gtk_notebook_prev_page(GTK_NOTEBOOK(playlist_notebook));
3571 }
3572 }
3573
3574 void
playlist_notebook_next_page(void)3575 playlist_notebook_next_page(void) {
3576
3577 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(playlist_notebook)) ==
3578 gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) - 1) {
3579 gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), 0);
3580 } else {
3581 gtk_notebook_next_page(GTK_NOTEBOOK(playlist_notebook));
3582 }
3583 }
3584
3585
3586 void
playlist_tab_close(GtkButton * button,gpointer data)3587 playlist_tab_close(GtkButton * button, gpointer data) {
3588
3589 playlist_t * pl = (playlist_t *)data;
3590 int n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook));
3591 int i;
3592
3593 if (pl->progbar_semaphore > 0 || pl->ms_semaphore > 0) {
3594 return;
3595 }
3596
3597 for (i = 0; i < n; i++) {
3598 if (pl->widget == gtk_notebook_get_nth_page(GTK_NOTEBOOK(playlist_notebook), i)) {
3599 gtk_notebook_remove_page(GTK_NOTEBOOK(playlist_notebook), i);
3600
3601 if (playlist_is_empty(pl)) {
3602 playlist_free(pl);
3603 } else {
3604 pl->closed = 1;
3605 pl->index = i;
3606 playlists_closed = g_list_prepend(playlists_closed, pl);
3607 playlists = g_list_remove(playlists, pl);
3608 }
3609
3610 if (!options.playlist_always_show_tabs &&
3611 gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) == 1) {
3612 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(playlist_notebook), FALSE);
3613 }
3614
3615 break;
3616 }
3617 }
3618
3619 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) == 0) {
3620 playlist_tab_new(NULL);
3621 }
3622 }
3623
3624 void
playlist_tab_close_undo(void)3625 playlist_tab_close_undo(void) {
3626
3627 GList * first = g_list_first(playlists_closed);
3628
3629 if (first != NULL) {
3630 playlist_t * pl = (playlist_t *)first->data;
3631
3632 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) == 1) {
3633 playlist_t * pl = (playlist_t *)playlists->data;
3634 if (playlist_is_empty(pl)) {
3635 gtk_notebook_remove_page(GTK_NOTEBOOK(playlist_notebook), 0);
3636 playlist_free(pl);
3637 }
3638 }
3639
3640 if (playlist_get_playing() != NULL) {
3641 pl->playing = 0;
3642 }
3643
3644 pl->closed = 0;
3645 playlists_closed = g_list_delete_link(playlists_closed, first);
3646 playlists = g_list_insert(playlists, pl, pl->index);
3647 create_playlist_gui(pl);
3648 }
3649 }
3650
3651 gint
playlist_notebook_clicked(GtkWidget * widget,GdkEventButton * event,gpointer data)3652 playlist_notebook_clicked(GtkWidget * widget, GdkEventButton * event, gpointer data) {
3653
3654 if (event->type == GDK_2BUTTON_PRESS && event->button == 1 &&
3655 event->y < 25 && event->x > 25 && event->x < widget->allocation.width - 25) {
3656 playlist_tab_new(NULL);
3657 return TRUE;
3658 }
3659
3660 return FALSE;
3661 }
3662
3663 gint
playlist_tab_label_clicked(GtkWidget * widget,GdkEventButton * event,gpointer data)3664 playlist_tab_label_clicked(GtkWidget * widget, GdkEventButton * event, gpointer data) {
3665
3666 playlist_t * pl = (playlist_t *)data;
3667
3668 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
3669
3670 GtkTreePath * path = playlist_get_playing_path(pl);
3671
3672 if (path != NULL) {
3673 playlist_start_playback_at_path(pl, path);
3674 gtk_tree_path_free(path);
3675 } else {
3676 GtkTreeIter iter;
3677 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pl->store), &iter)) {
3678 path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl->store), &iter);
3679 playlist_start_playback_at_path(pl, path);
3680 gtk_tree_path_free(path);
3681 }
3682 }
3683
3684 return TRUE;
3685 }
3686
3687 if (event->button == 2) {
3688 playlist_tab_close(NULL, pl);
3689 return TRUE;
3690 }
3691
3692 if (event->button == 3) {
3693 if (g_list_length(playlists_closed) > 0) {
3694 gtk_widget_set_sensitive(pl->tab__close_undo, TRUE);
3695 } else {
3696 gtk_widget_set_sensitive(pl->tab__close_undo, FALSE);
3697 }
3698 gtk_menu_popup(GTK_MENU(pl->tab_menu), NULL, NULL, NULL, NULL,
3699 event->button, event->time);
3700 return TRUE;
3701 }
3702
3703 return FALSE;
3704 }
3705
3706 gint
tab__rename_key_press_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)3707 tab__rename_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data) {
3708
3709 if (event->keyval == GDK_Return) {
3710 gtk_dialog_response (GTK_DIALOG(widget), GTK_RESPONSE_ACCEPT);
3711 return TRUE;
3712 }
3713
3714 return FALSE;
3715 }
3716
3717 void
tab__rename_cb(gpointer data)3718 tab__rename_cb(gpointer data) {
3719
3720 GtkWidget * dialog;
3721 GtkWidget * table;
3722 GtkWidget * hbox;
3723 GtkWidget * hbox2;
3724 GtkWidget * entry;
3725 GtkWidget * label;
3726
3727 char name[MAXLEN];
3728 playlist_t * pl = (playlist_t *)data;
3729
3730
3731 dialog = gtk_dialog_new_with_buttons(_("Rename playlist"),
3732 options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
3733 GTK_DIALOG_DESTROY_WITH_PARENT,
3734 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3735 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
3736 NULL);
3737
3738 gtk_widget_set_size_request(GTK_WIDGET(dialog), 400, -1);
3739 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
3740 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_REJECT);
3741 g_signal_connect (G_OBJECT (dialog), "key_press_event",
3742 G_CALLBACK (tab__rename_key_press_cb), NULL);
3743
3744 table = gtk_table_new(2, 2, FALSE);
3745 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
3746 table, FALSE, TRUE, 2);
3747
3748 label = gtk_label_new(_("Name:"));
3749 hbox = gtk_hbox_new(FALSE, 0);
3750 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3751 gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 5, 5);
3752
3753 hbox2 = gtk_hbox_new(FALSE, 0);
3754 gtk_table_attach(GTK_TABLE(table), hbox2, 1, 2, 0, 1,
3755 GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 5);
3756
3757 entry = gtk_entry_new();
3758 gtk_entry_set_max_length(GTK_ENTRY(entry), MAXLEN - 1);
3759 gtk_entry_set_text(GTK_ENTRY(entry), pl->name);
3760 gtk_box_pack_start(GTK_BOX(hbox2), entry, TRUE, TRUE, 0);
3761
3762 gtk_widget_grab_focus(entry);
3763
3764 gtk_widget_show_all(dialog);
3765
3766 display:
3767 name[0] = '\0';
3768 if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
3769
3770 strncpy(name, gtk_entry_get_text(GTK_ENTRY(entry)), MAXLEN-1);
3771
3772 if (name[0] == '\0') {
3773 gtk_widget_grab_focus(entry);
3774 goto display;
3775 }
3776
3777 playlist_rename(pl, name);
3778 }
3779
3780 gtk_widget_destroy(dialog);
3781 }
3782
3783
3784 void
tab__new_cb(gpointer data)3785 tab__new_cb(gpointer data) {
3786
3787 playlist_tab_new(NULL);
3788 }
3789
3790 void
tab__close_cb(gpointer data)3791 tab__close_cb(gpointer data) {
3792
3793 playlist_t * pl = (playlist_t *)data;
3794
3795 playlist_tab_close(NULL, pl);
3796 }
3797
3798 void
tab__close_undo_cb(gpointer data)3799 tab__close_undo_cb(gpointer data) {
3800
3801 playlist_tab_close_undo();
3802 }
3803
3804 void
tab__close_other_cb(gpointer data)3805 tab__close_other_cb(gpointer data) {
3806
3807 playlist_t * pl = (playlist_t *)data;
3808 GList * node;
3809
3810 for (node = playlists; node && node->data != pl; node = playlists) {
3811 playlist_tab_close(NULL, node->data);
3812 }
3813
3814 for (node = playlists->next; node && node->data != pl; node = playlists->next) {
3815 playlist_tab_close(NULL, node->data);
3816 }
3817 }
3818
3819 void
playlist_show_hide_close_buttons(gboolean state)3820 playlist_show_hide_close_buttons(gboolean state) {
3821
3822 GList * list;
3823
3824 for (list = playlists; list; list = list->next) {
3825 playlist_t * pl = (playlist_t *)list->data;
3826 if (state == TRUE) {
3827 gtk_widget_show(pl->tab_close_button);
3828 } else {
3829 gtk_widget_hide(pl->tab_close_button);
3830 }
3831 }
3832 }
3833
3834 GtkWidget *
create_playlist_tab_label(playlist_t * pl)3835 create_playlist_tab_label(playlist_t * pl) {
3836
3837 GtkWidget * hbox;
3838 GtkWidget * event_box;
3839 GtkWidget * image;
3840 GdkPixbuf * pixbuf;
3841
3842 GtkWidget * separator;
3843 GtkWidget * tab__new;
3844 GtkWidget * tab__rename;
3845 GtkWidget * tab__close;
3846 GtkWidget * tab__close_other;
3847
3848 char path[MAXLEN];
3849
3850
3851 hbox = gtk_hbox_new(FALSE, 4);
3852 gtk_widget_set_can_focus(hbox, FALSE);
3853
3854 event_box = gtk_event_box_new();
3855 pl->label = gtk_label_new(pl->name);
3856 gtk_widget_set_name(pl->label, "playlist_tab_label");
3857 gtk_container_add(GTK_CONTAINER(event_box), hbox);
3858
3859 pl->tab_close_button = gtk_button_new();
3860 gtk_widget_set_name(pl->tab_close_button, "playlist_tab_close_button");
3861 sprintf(path, "%s/tab-close.png", AQUALUNG_DATADIR);
3862 if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
3863 image = gtk_image_new_from_pixbuf(pixbuf);
3864 gtk_container_add(GTK_CONTAINER(pl->tab_close_button), image);
3865 }
3866
3867 gtk_button_set_relief(GTK_BUTTON(pl->tab_close_button), GTK_RELIEF_NONE);
3868 gtk_widget_set_can_focus(pl->tab_close_button, FALSE);
3869 gtk_widget_set_size_request(pl->tab_close_button, 16, 16);
3870
3871 gtk_box_pack_start(GTK_BOX(hbox), pl->label, TRUE, TRUE, 0);
3872 gtk_box_pack_start(GTK_BOX(hbox), pl->tab_close_button, FALSE, FALSE, 0);
3873
3874 gtk_widget_show_all(hbox);
3875
3876 g_signal_connect(G_OBJECT(pl->tab_close_button), "clicked", G_CALLBACK(playlist_tab_close), pl);
3877
3878 g_signal_connect(G_OBJECT(event_box), "button_press_event",
3879 G_CALLBACK(playlist_tab_label_clicked), pl);
3880
3881 /* popup menu for tabs */
3882
3883 pl->tab_menu = gtk_menu_new();
3884 tab__new = gtk_menu_item_new_with_label(_("New tab"));
3885 tab__rename = gtk_menu_item_new_with_label(_("Rename"));
3886 tab__close = gtk_menu_item_new_with_label(_("Close tab"));
3887 pl->tab__close_undo = gtk_menu_item_new_with_label(_("Undo close tab"));
3888 tab__close_other = gtk_menu_item_new_with_label(_("Close other tabs"));
3889
3890 gtk_menu_shell_append(GTK_MENU_SHELL(pl->tab_menu), tab__new);
3891 gtk_menu_shell_append(GTK_MENU_SHELL(pl->tab_menu), tab__rename);
3892
3893 separator = gtk_separator_menu_item_new();
3894 gtk_menu_shell_append(GTK_MENU_SHELL(pl->tab_menu), separator);
3895 gtk_widget_show(separator);
3896
3897 gtk_menu_shell_append(GTK_MENU_SHELL(pl->tab_menu), tab__close);
3898 gtk_menu_shell_append(GTK_MENU_SHELL(pl->tab_menu), pl->tab__close_undo);
3899
3900 separator = gtk_separator_menu_item_new();
3901 gtk_menu_shell_append(GTK_MENU_SHELL(pl->tab_menu), separator);
3902 gtk_widget_show(separator);
3903
3904 gtk_menu_shell_append(GTK_MENU_SHELL(pl->tab_menu), tab__close_other);
3905
3906 g_signal_connect_swapped(G_OBJECT(tab__new), "activate", G_CALLBACK(tab__new_cb), pl);
3907 g_signal_connect_swapped(G_OBJECT(tab__rename), "activate", G_CALLBACK(tab__rename_cb), pl);
3908 g_signal_connect_swapped(G_OBJECT(tab__close), "activate", G_CALLBACK(tab__close_cb), pl);
3909 g_signal_connect_swapped(G_OBJECT(pl->tab__close_undo), "activate", G_CALLBACK(tab__close_undo_cb), pl);
3910 g_signal_connect_swapped(G_OBJECT(tab__close_other), "activate", G_CALLBACK(tab__close_other_cb), pl);
3911
3912 gtk_widget_show(tab__rename);
3913 gtk_widget_show(tab__new);
3914 gtk_widget_show(tab__close);
3915 gtk_widget_show(pl->tab__close_undo);
3916 gtk_widget_show(tab__close_other);
3917
3918 if (options.playlist_show_close_button_in_tab == FALSE) {
3919 gtk_widget_hide(pl->tab_close_button);
3920 }
3921
3922 return event_box;
3923 }
3924
3925 void
create_playlist_gui(playlist_t * pl)3926 create_playlist_gui(playlist_t * pl) {
3927
3928 GtkWidget * viewport;
3929 GtkCellRenderer * track_renderer;
3930 GtkCellRenderer * rva_renderer;
3931 GtkCellRenderer * length_renderer;
3932
3933 int i;
3934
3935 GdkPixbuf * pixbuf;
3936 gchar path[MAXLEN];
3937
3938 GtkTargetEntry source_table[] = {
3939 { "aqualung-list-list", GTK_TARGET_SAME_APP, 0 }
3940 };
3941
3942 GtkTargetEntry target_table[] = {
3943 { "aqualung-list-list", GTK_TARGET_SAME_APP, 0 },
3944 { "aqualung-browser-list", GTK_TARGET_SAME_APP, 1 },
3945 { "STRING", 0, 2 },
3946 { "text/plain", 0, 2 },
3947 { "text/uri-list", 0, 2 }
3948 };
3949
3950 pl->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pl->store));
3951 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(pl->view), FALSE);
3952 gtk_widget_set_name(pl->view, "play_list");
3953 gtk_widget_set_size_request(pl->view, 100, 100);
3954
3955 g_signal_connect(G_OBJECT(pl->view), "key_press_event",
3956 G_CALLBACK(playlist_key_pressed), NULL);
3957
3958 if (options.override_skin_settings) {
3959 gtk_widget_modify_font(pl->view, fd_playlist);
3960 }
3961
3962 if (options.enable_pl_rules_hint) {
3963 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(pl->view), TRUE);
3964 }
3965
3966 for (i = 0; i < 3; i++) {
3967 switch (options.plcol_idx[i]) {
3968 case 0:
3969 track_renderer = gtk_cell_renderer_text_new();
3970 g_object_set((gpointer)track_renderer, "xalign", 0.0, NULL);
3971 pl->track_column = gtk_tree_view_column_new_with_attributes("Tracks",
3972 track_renderer,
3973 "text", PL_COL_NAME,
3974 "foreground", PL_COL_COLO,
3975 "weight", PL_COL_FONT,
3976 NULL);
3977 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(pl->track_column),
3978 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
3979 gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(pl->track_column), 3);
3980 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(pl->track_column), FALSE);
3981 g_object_set(G_OBJECT(track_renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
3982 gtk_tree_view_column_set_expand(GTK_TREE_VIEW_COLUMN(pl->track_column), TRUE);
3983 gtk_tree_view_append_column(GTK_TREE_VIEW(pl->view), pl->track_column);
3984 break;
3985
3986 case 1:
3987 rva_renderer = gtk_cell_renderer_text_new();
3988 g_object_set((gpointer)rva_renderer, "xalign", 1.0, NULL);
3989 pl->rva_column = gtk_tree_view_column_new_with_attributes("RVA",
3990 rva_renderer,
3991 "text", PL_COL_VADJ,
3992 "foreground", PL_COL_COLO,
3993 "weight", PL_COL_FONT,
3994 NULL);
3995 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(pl->rva_column),
3996 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
3997 gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(pl->rva_column), 3);
3998 if (options.show_rva_in_playlist) {
3999 gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(pl->rva_column), TRUE);
4000 } else {
4001 gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(pl->rva_column), FALSE);
4002 }
4003 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(pl->rva_column), FALSE);
4004 gtk_tree_view_append_column(GTK_TREE_VIEW(pl->view), pl->rva_column);
4005 break;
4006
4007 case 2:
4008 length_renderer = gtk_cell_renderer_text_new();
4009 g_object_set((gpointer)length_renderer, "xalign", 1.0, NULL);
4010 pl->length_column = gtk_tree_view_column_new_with_attributes("Length",
4011 length_renderer,
4012 "text", PL_COL_DURA,
4013 "foreground", PL_COL_COLO,
4014 "weight", PL_COL_FONT,
4015 NULL);
4016 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(pl->length_column),
4017 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
4018 gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(pl->length_column), 3);
4019 if (options.show_length_in_playlist) {
4020 gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(pl->length_column), TRUE);
4021 } else {
4022 gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(pl->length_column), FALSE);
4023 }
4024 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(pl->length_column), FALSE);
4025 gtk_tree_view_append_column(GTK_TREE_VIEW(pl->view), pl->length_column);
4026 }
4027 }
4028
4029 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pl->view), FALSE);
4030
4031 g_signal_connect(G_OBJECT(pl->view), "button_press_event",
4032 G_CALLBACK(playlist_window_button_pressed), pl);
4033
4034
4035 /* setup drag and drop */
4036
4037 gtk_drag_source_set(pl->view,
4038 GDK_BUTTON1_MASK,
4039 source_table,
4040 sizeof(source_table) / sizeof(GtkTargetEntry),
4041 GDK_ACTION_MOVE);
4042
4043 gtk_drag_dest_set(pl->view,
4044 GTK_DEST_DEFAULT_ALL,
4045 target_table,
4046 sizeof(target_table) / sizeof(GtkTargetEntry),
4047 GDK_ACTION_MOVE | GDK_ACTION_COPY);
4048
4049 snprintf(path, MAXLEN-1, "%s/drag.png", AQUALUNG_DATADIR);
4050 if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
4051 gtk_drag_source_set_icon_pixbuf(pl->view, pixbuf);
4052 }
4053
4054 g_signal_connect(G_OBJECT(pl->view), "drag_end",
4055 G_CALLBACK(playlist_drag_end), pl);
4056
4057 g_signal_connect(G_OBJECT(pl->view), "drag_data_get",
4058 G_CALLBACK(playlist_drag_data_get), pl);
4059
4060 g_signal_connect(G_OBJECT(pl->view), "drag_leave",
4061 G_CALLBACK(playlist_drag_leave), pl);
4062
4063 g_signal_connect(G_OBJECT(pl->view), "drag_motion",
4064 G_CALLBACK(playlist_drag_motion), pl);
4065
4066 g_signal_connect(G_OBJECT(pl->view), "drag_data_received",
4067 G_CALLBACK(playlist_drag_data_received), pl);
4068
4069
4070 pl->select = gtk_tree_view_get_selection(GTK_TREE_VIEW(pl->view));
4071 gtk_tree_selection_set_mode(pl->select, GTK_SELECTION_MULTIPLE);
4072
4073 g_signal_connect(G_OBJECT(pl->select), "changed",
4074 G_CALLBACK(playlist_selection_changed_cb), pl);
4075
4076
4077 pl->widget = gtk_vbox_new(FALSE, 2);
4078
4079 viewport = gtk_viewport_new(NULL, NULL);
4080 gtk_box_pack_start(GTK_BOX(pl->widget), viewport, TRUE, TRUE, 0);
4081
4082 pl->scroll = gtk_scrolled_window_new(NULL, NULL);
4083 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pl->scroll),
4084 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
4085 gtk_container_add(GTK_CONTAINER(viewport), pl->scroll);
4086
4087 gtk_container_add(GTK_CONTAINER(pl->scroll), pl->view);
4088
4089
4090 pl->progbar_container = gtk_hbox_new(FALSE, 4);
4091 gtk_box_pack_start(GTK_BOX(pl->widget), pl->progbar_container, FALSE, FALSE, 0);
4092
4093 if (pl->progbar_semaphore > 0) {
4094 pl->progbar_semaphore--;
4095 playlist_progress_bar_show(pl);
4096 }
4097
4098 gtk_widget_show_all(pl->widget);
4099
4100 gtk_notebook_insert_page(GTK_NOTEBOOK(playlist_notebook),
4101 pl->widget,
4102 create_playlist_tab_label(pl),
4103 pl->index);
4104
4105 gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), pl->index);
4106
4107 playlist_set_playing(pl, pl->playing);
4108
4109 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) > 1) {
4110 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(playlist_notebook), TRUE);
4111 }
4112
4113 gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(playlist_notebook), pl->widget, TRUE);
4114
4115 gtk_widget_grab_focus(pl->view);
4116 }
4117
4118 playlist_t *
playlist_tab_new(char * name)4119 playlist_tab_new(char * name) {
4120
4121 playlist_t * pl = NULL;
4122
4123 pl = playlist_new(name);
4124 create_playlist_gui(pl);
4125 return pl;
4126 }
4127
4128 playlist_t *
playlist_tab_new_if_nonempty(char * name)4129 playlist_tab_new_if_nonempty(char * name) {
4130
4131 playlist_t * pl = (playlist_t *)playlists->data;
4132
4133 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) == 1 &&
4134 playlist_is_empty(pl)) {
4135 playlist_rename(pl, name);
4136 return pl;
4137 }
4138
4139 return playlist_tab_new(name);
4140 }
4141
4142 void
notebook_switch_page(GtkNotebook * notebook,GtkNotebookPage * page,guint page_num,gpointer user_data)4143 notebook_switch_page(GtkNotebook * notebook, GtkNotebookPage * page,
4144 guint page_num, gpointer user_data) {
4145
4146 playlist_t * pl = playlist_get_by_page_num(page_num);
4147
4148 if (pl == NULL) {
4149 return;
4150 }
4151
4152 playlist_content_changed(pl);
4153 playlist_selection_changed(pl);
4154 }
4155
4156 void
notebook_page_reordered(GtkNotebook * notebook,GtkWidget * child,guint page_num,gpointer user_data)4157 notebook_page_reordered(GtkNotebook * notebook, GtkWidget * child,
4158 guint page_num, gpointer user_data) {
4159
4160 GList * node;
4161
4162 for (node = playlists; node; node = node->next) {
4163 playlist_t * pl = (playlist_t *)node->data;
4164 pl->index = gtk_notebook_page_num(notebook, pl->widget);
4165 }
4166
4167 playlists = g_list_sort(playlists, playlist_compare_index);
4168 }
4169
4170 void
create_playlist(void)4171 create_playlist(void) {
4172
4173 GtkWidget * vbox;
4174
4175 GtkWidget * statusbar;
4176 GtkWidget * statusbar_scrolledwin;
4177 GtkWidget * statusbar_viewport;
4178
4179 GtkWidget * hbox_bottom;
4180 GtkWidget * add_button;
4181 GtkWidget * sel_button;
4182 GtkWidget * rem_button;
4183
4184
4185 if (pl_color_active[0] == '\0') {
4186 strcpy(pl_color_active, "#000080");
4187 }
4188
4189 if (pl_color_inactive[0] == '\0') {
4190 strcpy(pl_color_inactive, "#010101");
4191 }
4192
4193
4194 /* window creating stuff */
4195 if (!options.playlist_is_embedded) {
4196 playlist_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4197 gtk_window_set_title(GTK_WINDOW(playlist_window), _("Playlist"));
4198 gtk_container_set_border_width(GTK_CONTAINER(playlist_window), 2);
4199 g_signal_connect(G_OBJECT(playlist_window), "delete_event",
4200 G_CALLBACK(playlist_window_close), NULL);
4201 gtk_widget_set_size_request(playlist_window, 300, 200);
4202 } else {
4203 gtk_widget_set_size_request(playlist_window, 200, 200);
4204 }
4205
4206 if (!options.playlist_is_embedded) {
4207 g_signal_connect(G_OBJECT(playlist_window), "key_press_event",
4208 G_CALLBACK(playlist_window_key_pressed), NULL);
4209 }
4210
4211 gtk_widget_set_events(playlist_window, GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
4212
4213
4214 if (!options.playlist_is_embedded) {
4215 plist_menu = gtk_menu_new();
4216 register_toplevel_window(plist_menu, TOP_WIN_SKIN);
4217 init_plist_menu(plist_menu);
4218 }
4219
4220 vbox = gtk_vbox_new(FALSE, 2);
4221 gtk_container_add(GTK_CONTAINER(playlist_window), vbox);
4222
4223 /* this widget should not be visually noticeable, it is only used for
4224 getting active and inactive playlist colors from the skin/rc file */
4225 playlist_color_indicator = gtk_hbox_new(FALSE, 0);
4226 gtk_widget_set_name(playlist_color_indicator, "playlist_color_indicator");
4227 gtk_box_pack_start(GTK_BOX(vbox), playlist_color_indicator, FALSE, FALSE, 0);
4228
4229 /* create notebook */
4230 playlist_notebook = gtk_notebook_new();
4231 gtk_notebook_set_show_border(GTK_NOTEBOOK(playlist_notebook), FALSE);
4232 gtk_notebook_set_scrollable(GTK_NOTEBOOK(playlist_notebook), TRUE);
4233 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(playlist_notebook),
4234 options.playlist_always_show_tabs);
4235 gtk_box_pack_start(GTK_BOX(vbox), playlist_notebook, TRUE, TRUE, 0);
4236
4237 g_signal_connect(G_OBJECT(playlist_notebook), "key_press_event",
4238 G_CALLBACK(playlist_notebook_key_pressed), NULL);
4239
4240 g_signal_connect(G_OBJECT(playlist_notebook), "button_press_event",
4241 G_CALLBACK(playlist_notebook_clicked), NULL);
4242
4243 g_signal_connect(G_OBJECT(playlist_notebook), "switch_page",
4244 G_CALLBACK(notebook_switch_page), NULL);
4245
4246 g_signal_connect(G_OBJECT(playlist_notebook), "page_reordered",
4247 G_CALLBACK(notebook_page_reordered), NULL);
4248
4249 if (playlists != NULL) {
4250 GList * list;
4251 for (list = playlists; list; list = list->next) {
4252 playlist_t * pl = (playlist_t *)list->data;
4253 if (!pl->closed) {
4254 create_playlist_gui(pl);
4255 }
4256 }
4257 }
4258
4259
4260 /* statusbar */
4261 if (options.enable_playlist_statusbar) {
4262
4263 statusbar_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
4264 gtk_widget_set_can_focus(statusbar_scrolledwin, FALSE);
4265 gtk_widget_set_size_request(statusbar_scrolledwin, 1, -1); /* MAGIC */
4266 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(statusbar_scrolledwin),
4267 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
4268
4269 statusbar_viewport = gtk_viewport_new(NULL, NULL);
4270 gtk_widget_set_name(statusbar_viewport, "info_viewport");
4271
4272 gtk_container_add(GTK_CONTAINER(statusbar_scrolledwin), statusbar_viewport);
4273 gtk_box_pack_start(GTK_BOX(vbox), statusbar_scrolledwin, FALSE, TRUE, 2);
4274
4275 gtk_widget_set_events(statusbar_viewport,
4276 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
4277
4278 g_signal_connect(G_OBJECT(statusbar_viewport), "button_press_event",
4279 G_CALLBACK(scroll_btn_pressed), NULL);
4280 g_signal_connect(G_OBJECT(statusbar_viewport), "button_release_event",
4281 G_CALLBACK(scroll_btn_released), (gpointer)statusbar_scrolledwin);
4282 g_signal_connect(G_OBJECT(statusbar_viewport), "motion_notify_event",
4283 G_CALLBACK(scroll_motion_notify), (gpointer)statusbar_scrolledwin);
4284
4285 statusbar = gtk_hbox_new(FALSE, 0);
4286 gtk_container_set_border_width(GTK_CONTAINER(statusbar), 1);
4287 gtk_container_add(GTK_CONTAINER(statusbar_viewport), statusbar);
4288
4289 statusbar_selected = gtk_label_new("");
4290 gtk_widget_set_name(statusbar_selected, "label_info");
4291 gtk_box_pack_end(GTK_BOX(statusbar), statusbar_selected, FALSE, TRUE, 0);
4292
4293 statusbar_selected_label = gtk_label_new(_(" Selected: "));
4294 gtk_widget_set_name(statusbar_selected_label, "label_info");
4295 gtk_box_pack_end(GTK_BOX(statusbar), statusbar_selected_label, FALSE, TRUE, 0);
4296
4297 gtk_box_pack_end(GTK_BOX(statusbar), gtk_vseparator_new(), FALSE, TRUE, 5);
4298
4299 statusbar_total = gtk_label_new("");
4300 gtk_widget_set_name(statusbar_total, "label_info");
4301 gtk_box_pack_end(GTK_BOX(statusbar), statusbar_total, FALSE, TRUE, 0);
4302
4303 statusbar_total_label = gtk_label_new(_("Total: "));
4304 gtk_widget_set_name(statusbar_total_label, "label_info");
4305 gtk_box_pack_end(GTK_BOX(statusbar), statusbar_total_label, FALSE, TRUE, 0);
4306 }
4307
4308
4309 playlist_set_font(options.override_skin_settings);
4310
4311 /* bottom area of playlist window */
4312 hbox_bottom = gtk_hbox_new(FALSE, 0);
4313 gtk_box_pack_start(GTK_BOX(vbox), hbox_bottom, FALSE, TRUE, 0);
4314
4315 add_button = gtk_button_new_with_label(_("Add files"));
4316 gtk_widget_set_can_focus(add_button, FALSE);
4317 aqualung_widget_set_tooltip_text(add_button, _("Add files to playlist\n(Press right mouse button for menu)"));
4318 gtk_box_pack_start(GTK_BOX(hbox_bottom), add_button, TRUE, TRUE, 0);
4319 g_signal_connect(G_OBJECT(add_button), "clicked", G_CALLBACK(add_files), NULL);
4320
4321 sel_button = gtk_button_new_with_label(_("Select all"));
4322 gtk_widget_set_can_focus(sel_button, FALSE);
4323 aqualung_widget_set_tooltip_text(sel_button, _("Select all songs in playlist\n(Press right mouse button for menu)"));
4324 gtk_box_pack_start(GTK_BOX(hbox_bottom), sel_button, TRUE, TRUE, 0);
4325 g_signal_connect(G_OBJECT(sel_button), "clicked", G_CALLBACK(select_all), NULL);
4326
4327 rem_button = gtk_button_new_with_label(_("Remove selected"));
4328 gtk_widget_set_can_focus(rem_button, FALSE);
4329 aqualung_widget_set_tooltip_text(rem_button, _("Remove selected songs from playlist\n(Press right mouse button for menu)"));
4330 gtk_box_pack_start(GTK_BOX(hbox_bottom), rem_button, TRUE, TRUE, 0);
4331 g_signal_connect(G_OBJECT(rem_button), "clicked", G_CALLBACK(remove_sel), NULL);
4332
4333
4334 add_menu = gtk_menu_new();
4335 register_toplevel_window(add_menu, TOP_WIN_SKIN);
4336
4337 add__dir = gtk_menu_item_new_with_label(_("Add directory"));
4338 gtk_menu_shell_append(GTK_MENU_SHELL(add_menu), add__dir);
4339 g_signal_connect_swapped(G_OBJECT(add__dir), "activate", G_CALLBACK(add__dir_cb), NULL);
4340 gtk_widget_show(add__dir);
4341
4342 add__url = gtk_menu_item_new_with_label(_("Add URL"));
4343 gtk_menu_shell_append(GTK_MENU_SHELL(add_menu), add__url);
4344 g_signal_connect_swapped(G_OBJECT(add__url), "activate", G_CALLBACK(add__url_cb), NULL);
4345 gtk_widget_show(add__url);
4346
4347 add__files = gtk_menu_item_new_with_label(_("Add files"));
4348 gtk_menu_shell_append(GTK_MENU_SHELL(add_menu), add__files);
4349 g_signal_connect_swapped(G_OBJECT(add__files), "activate", G_CALLBACK(add__files_cb), NULL);
4350 gtk_widget_show(add__files);
4351
4352 g_signal_connect_swapped(G_OBJECT(add_button), "event", G_CALLBACK(add_cb), NULL);
4353
4354
4355 sel_menu = gtk_menu_new();
4356 register_toplevel_window(sel_menu, TOP_WIN_SKIN);
4357
4358 sel__none = gtk_menu_item_new_with_label(_("Select none"));
4359 gtk_menu_shell_append(GTK_MENU_SHELL(sel_menu), sel__none);
4360 g_signal_connect_swapped(G_OBJECT(sel__none), "activate", G_CALLBACK(sel__none_cb), NULL);
4361 gtk_widget_show(sel__none);
4362
4363 sel__inv = gtk_menu_item_new_with_label(_("Invert selection"));
4364 gtk_menu_shell_append(GTK_MENU_SHELL(sel_menu), sel__inv);
4365 g_signal_connect_swapped(G_OBJECT(sel__inv), "activate", G_CALLBACK(sel__inv_cb), NULL);
4366 gtk_widget_show(sel__inv);
4367
4368 sel__all = gtk_menu_item_new_with_label(_("Select all"));
4369 gtk_menu_shell_append(GTK_MENU_SHELL(sel_menu), sel__all);
4370 g_signal_connect_swapped(G_OBJECT(sel__all), "activate", G_CALLBACK(sel__all_cb), NULL);
4371 gtk_widget_show(sel__all);
4372
4373 g_signal_connect_swapped(G_OBJECT(sel_button), "event", G_CALLBACK(sel_cb), NULL);
4374
4375
4376 rem_menu = gtk_menu_new();
4377 register_toplevel_window(rem_menu, TOP_WIN_SKIN);
4378
4379 rem__all = gtk_menu_item_new_with_label(_("Remove all"));
4380 gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), rem__all);
4381 g_signal_connect_swapped(G_OBJECT(rem__all), "activate", G_CALLBACK(rem__all_cb), NULL);
4382 gtk_widget_show(rem__all);
4383
4384 rem__dead = gtk_menu_item_new_with_label(_("Remove dead"));
4385 gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), rem__dead);
4386 g_signal_connect_swapped(G_OBJECT(rem__dead), "activate", G_CALLBACK(rem__dead_cb), NULL);
4387 gtk_widget_show(rem__dead);
4388
4389 cut__sel = gtk_menu_item_new_with_label(_("Cut selected"));
4390 gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), cut__sel);
4391 g_signal_connect_swapped(G_OBJECT(cut__sel), "activate", G_CALLBACK(cut__sel_cb), NULL);
4392 gtk_widget_show(cut__sel);
4393
4394 rem__sel = gtk_menu_item_new_with_label(_("Remove selected"));
4395 gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), rem__sel);
4396 g_signal_connect_swapped(G_OBJECT(rem__sel), "activate", G_CALLBACK(rem__sel_cb), NULL);
4397 gtk_widget_show(rem__sel);
4398
4399 g_signal_connect_swapped(G_OBJECT(rem_button), "event", G_CALLBACK(rem_cb), NULL);
4400 }
4401
4402
4403 void
playlist_ensure_tab_exists()4404 playlist_ensure_tab_exists() {
4405
4406 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook)) == 0) {
4407 playlist_tab_new(NULL);
4408 }
4409 }
4410
4411
4412 void
show_playlist(void)4413 show_playlist(void) {
4414
4415 options.playlist_on = 1;
4416
4417 if (!options.playlist_is_embedded) {
4418 gtk_window_move(GTK_WINDOW(playlist_window), options.playlist_pos_x, options.playlist_pos_y);
4419 gtk_window_resize(GTK_WINDOW(playlist_window), options.playlist_size_x, options.playlist_size_y);
4420 register_toplevel_window(playlist_window, TOP_WIN_SKIN | TOP_WIN_TRAY);
4421 } else {
4422 gtk_window_get_size(GTK_WINDOW(main_window), &options.main_size_x, &options.main_size_y);
4423 gtk_window_resize(GTK_WINDOW(main_window), options.main_size_x, options.main_size_y + options.playlist_size_y + 6);
4424 }
4425
4426 gtk_widget_show_all(playlist_window);
4427 }
4428
4429 void
hide_playlist(void)4430 hide_playlist(void) {
4431
4432 options.playlist_on = 0;
4433 if (!options.playlist_is_embedded) {
4434 gtk_window_get_position(GTK_WINDOW(playlist_window), &options.playlist_pos_x, &options.playlist_pos_y);
4435 gtk_window_get_size(GTK_WINDOW(playlist_window), &options.playlist_size_x, &options.playlist_size_y);
4436 register_toplevel_window(playlist_window, TOP_WIN_SKIN);
4437 } else {
4438 options.playlist_size_x = playlist_window->allocation.width;
4439 options.playlist_size_y = playlist_window->allocation.height;
4440 gtk_window_get_size(GTK_WINDOW(main_window), &options.main_size_x, &options.main_size_y);
4441 }
4442
4443 gtk_widget_hide(playlist_window);
4444
4445 if (options.playlist_is_embedded) {
4446 gtk_window_resize(GTK_WINDOW(main_window), options.main_size_x, options.main_size_y - options.playlist_size_y - 6);
4447 }
4448
4449 }
4450
4451
4452 xmlNodePtr
save_track_node(playlist_t * pl,GtkTreeIter * piter,xmlNodePtr root,char * nodeID)4453 save_track_node(playlist_t * pl, GtkTreeIter * piter, xmlNodePtr root, char * nodeID) {
4454
4455 gchar str[32];
4456 xmlNodePtr node;
4457 playlist_data_t * data;
4458
4459
4460 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), piter, PL_COL_DATA, &data, -1);
4461
4462 node = xmlNewTextChild(root, NULL, (const xmlChar*) nodeID, NULL);
4463 xmlNewTextChild(node, NULL, (const xmlChar*) "artist", (const xmlChar*) data->artist);
4464 xmlNewTextChild(node, NULL, (const xmlChar*) "album", (const xmlChar*) data->album);
4465
4466 if (strcmp(nodeID, "record")) {
4467
4468 char * file = NULL;
4469
4470 xmlNewTextChild(node, NULL, (const xmlChar*) "title", (const xmlChar*) data->title);
4471 xmlNewTextChild(node, NULL, (const xmlChar*) "display", (const xmlChar*) data->display);
4472
4473 if (g_str_has_prefix(data->file, "CDDA ") ||
4474 httpc_is_url(data->file)) {
4475 file = g_strdup(data->file);
4476 } else {
4477 file = g_filename_to_uri(data->file, NULL, NULL);
4478 }
4479 xmlNewTextChild(node, NULL, (const xmlChar*) "file", (const xmlChar*) file);
4480 g_free(file);
4481
4482 snprintf(str, 31, "%f", data->voladj);
4483 xmlNewTextChild(node, NULL, (const xmlChar*) "voladj", (const xmlChar*) str);
4484
4485 snprintf(str, 31, "%u", data->size);
4486 xmlNewTextChild(node, NULL, (const xmlChar*) "size", (const xmlChar*) str);
4487
4488 } else if (IS_PL_ACTIVE(data)){
4489
4490 snprintf(str, 31, "%u", data->ntracks);
4491 xmlNewTextChild(node, NULL, (const xmlChar*) "ntracks", (const xmlChar*) str);
4492
4493 snprintf(str, 31, "%u", data->actrack);
4494 xmlNewTextChild(node, NULL, (const xmlChar*) "actrack", (const xmlChar*) str);
4495 }
4496
4497 snprintf(str, 31, "%f", data->duration);
4498 xmlNewTextChild(node, NULL, (const xmlChar*) "duration", (const xmlChar*) str);
4499
4500 if (IS_PL_ACTIVE(data)) {
4501 xmlNewTextChild(node, NULL, (const xmlChar*) "active", NULL);
4502 }
4503
4504 if (IS_PL_COVER(data)) {
4505 xmlNewTextChild(node, NULL, (const xmlChar*) "cover", NULL);
4506 }
4507
4508 return node;
4509 }
4510
4511 void
playlist_save_data(playlist_t * pl,xmlNodePtr dest,int current)4512 playlist_save_data(playlist_t * pl, xmlNodePtr dest, int current) {
4513
4514 gint i = 0;
4515 GtkTreeIter iter;
4516 GtkTreePath * p = NULL;
4517
4518 if (pl->name_set) {
4519 xmlNewTextChild(dest, NULL, (const xmlChar*) "name", (const xmlChar*) pl->name);
4520 }
4521 if (pl->playing) {
4522 xmlNewTextChild(dest, NULL, (const xmlChar*) "active", NULL);
4523 }
4524 if (current) {
4525 xmlNewTextChild(dest, NULL, (const xmlChar*) "current", NULL);
4526 }
4527 if ((p = playlist_get_playing_path(pl)) != NULL) {
4528 xmlNewTextChild(dest, NULL, (const xmlChar*) "has_active_track", NULL);
4529 gtk_tree_path_free(p);
4530 }
4531
4532 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
4533
4534 gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter);
4535
4536 if (n) { /* album node */
4537 gint j = 0;
4538 GtkTreeIter iter_child;
4539 xmlNodePtr node;
4540
4541 node = save_track_node(pl, &iter, dest, "record");
4542 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store),
4543 &iter_child, &iter, j++)) {
4544 save_track_node(pl, &iter_child, node, "track");
4545 }
4546 } else { /* track node */
4547 save_track_node(pl, &iter, dest, "track");
4548 }
4549 }
4550 }
4551
4552
4553 void
playlist_save(playlist_t * pl,char * filename)4554 playlist_save(playlist_t * pl, char * filename) {
4555
4556 xmlDocPtr doc;
4557 xmlNodePtr root;
4558
4559 doc = xmlNewDoc((const xmlChar*) "1.0");
4560 root = xmlNewNode(NULL, (const xmlChar*) "aqualung_playlist");
4561 xmlDocSetRootElement(doc, root);
4562
4563 playlist_save_data(pl, root, 0);
4564
4565 xmlSaveFormatFile(filename, doc, 1);
4566 xmlFreeDoc(doc);
4567 }
4568
4569 int
playlist_save_m3u_node(playlist_t * pl,GtkTreeIter * iter,FILE * f)4570 playlist_save_m3u_node(playlist_t * pl, GtkTreeIter * iter, FILE * f) {
4571
4572 playlist_data_t * data;
4573 size_t fn_len;
4574
4575 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &data, -1);
4576 fn_len = strlen(data->file);
4577 if (fwrite(data->file, 1, fn_len, f) != fn_len) {
4578 g_warning("Error writing the playlist file");
4579 return -1;
4580 }
4581 if (fwrite("\n", 1, 1, f) != 1) {
4582 g_warning("Error writing the playlist file");
4583 return -1;
4584 }
4585 return 0;
4586 }
4587
4588 void
playlist_save_m3u(playlist_t * pl,char * filename)4589 playlist_save_m3u(playlist_t * pl, char * filename) {
4590
4591 FILE * f;
4592 gint i = 0;
4593 GtkTreeIter iter;
4594
4595 if ((f = g_fopen(filename, "wb")) == NULL) {
4596 g_warning("Unable to open playlist file %s for writing", filename);
4597 return;
4598 }
4599
4600 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
4601
4602 gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter);
4603
4604 if (n) { /* album node */
4605 gint j = 0;
4606 GtkTreeIter iter_child;
4607
4608 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store),
4609 &iter_child, &iter, j++)) {
4610 if (playlist_save_m3u_node(pl, &iter_child, f) != 0) {
4611 goto playlist_save_m3u_cleanup;
4612 }
4613 }
4614 } else { /* track node */
4615 if (playlist_save_m3u_node(pl, &iter, f) != 0) {
4616 goto playlist_save_m3u_cleanup;
4617 }
4618 }
4619 }
4620
4621 playlist_save_m3u_cleanup:
4622 if (fclose(f) != 0) {
4623 g_warning("Unable to close playlist file %s", filename);
4624 }
4625 }
4626
4627 void
playlist_save_all(char * filename)4628 playlist_save_all(char * filename) {
4629
4630 xmlDocPtr doc;
4631 xmlNodePtr root;
4632 xmlNodePtr tab;
4633
4634 GList * list = NULL;
4635 int current = gtk_notebook_get_current_page(GTK_NOTEBOOK(playlist_notebook));
4636 int i = 0;
4637
4638 doc = xmlNewDoc((const xmlChar*) "1.0");
4639 root = xmlNewNode(NULL, (const xmlChar*) "aqualung_playlist_tabs");
4640 xmlDocSetRootElement(doc, root);
4641
4642 for (list = playlists; list; list = list->next) {
4643 playlist_t * pl = (playlist_t *)list->data;
4644
4645 tab = xmlNewTextChild(root, NULL, (const xmlChar*) "tab", NULL);
4646 playlist_save_data(pl, tab, (i == current));
4647 i++;
4648 }
4649
4650 xmlSaveFormatFile(filename, doc, 1);
4651 xmlFreeDoc(doc);
4652 }
4653
4654 gboolean
playlist_auto_save_cb(gpointer data)4655 playlist_auto_save_cb(gpointer data) {
4656
4657 if (playlist_dirty) {
4658 char playlist_name[MAXLEN];
4659 snprintf(playlist_name, MAXLEN-1, "%s/%s", options.confdir, "playlist.xml");
4660 playlist_save_all(playlist_name);
4661 playlist_dirty = 0;
4662 }
4663
4664 return TRUE;
4665 }
4666
4667 void
playlist_auto_save_reset(void)4668 playlist_auto_save_reset(void) {
4669
4670 if (playlist_auto_save_tag != -1) {
4671 g_source_remove(playlist_auto_save_tag);
4672 }
4673 if (options.playlist_auto_save) {
4674 playlist_auto_save_tag = aqualung_timeout_add(options.playlist_auto_save_int * 60000,
4675 playlist_auto_save_cb, NULL);
4676 }
4677 }
4678
4679 xmlChar *
parse_playlist_name(xmlDocPtr doc,xmlNodePtr _cur)4680 parse_playlist_name(xmlDocPtr doc, xmlNodePtr _cur) {
4681
4682 xmlNodePtr cur;
4683
4684 for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4685 if (!xmlStrcmp(cur->name, (const xmlChar *)"name")) {
4686 return xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
4687 }
4688 }
4689
4690 return NULL;
4691 }
4692
4693 void
parse_playlist_status(xmlDocPtr doc,xmlNodePtr _cur,int * active,int * current,int * has_active)4694 parse_playlist_status(xmlDocPtr doc, xmlNodePtr _cur, int * active, int * current, int * has_active) {
4695
4696 xmlNodePtr cur;
4697
4698 if (active) {
4699 *active = 0;
4700 }
4701 if (current) {
4702 *current = 0;
4703 }
4704 if (has_active) {
4705 *has_active = 0;
4706 }
4707
4708 for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4709 if (active && !xmlStrcmp(cur->name, (const xmlChar *)"active")) {
4710 *active = 1;
4711 }
4712 if (current && !xmlStrcmp(cur->name, (const xmlChar *)"current")) {
4713 *current = 1;
4714 }
4715 if (has_active && !xmlStrcmp(cur->name, (const xmlChar *)"has_active_track")) {
4716 *has_active = 1;
4717 }
4718 }
4719 }
4720
4721 void
parse_playlist_track(playlist_transfer_t * pt,xmlDocPtr doc,xmlNodePtr _cur,int flags)4722 parse_playlist_track(playlist_transfer_t * pt, xmlDocPtr doc, xmlNodePtr _cur, int flags) {
4723
4724 xmlChar * key;
4725 xmlNodePtr cur;
4726 playlist_data_t * data = NULL;
4727
4728 if ((data = playlist_data_new()) == NULL) {
4729 return;
4730 }
4731
4732 data->flags = flags;
4733
4734 for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4735
4736 if (!xmlStrcmp(cur->name, (const xmlChar *)"artist")) {
4737 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4738 data->artist = strndup((char *)key, MAXLEN-1);
4739 xmlFree(key);
4740 }
4741 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"album")) {
4742 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4743 data->album = strndup((char *)key, MAXLEN-1);
4744 xmlFree(key);
4745 }
4746 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"title")) {
4747 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4748 data->title = strndup((char *)key, MAXLEN-1);
4749 xmlFree(key);
4750 }
4751 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"display")) {
4752 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4753 data->display = strndup((char *)key, MAXLEN-1);
4754 xmlFree(key);
4755 }
4756 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"file")) {
4757 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4758 gchar * tmp;
4759 if ((tmp = g_filename_from_uri((char *) key, NULL, NULL))) {
4760 data->file = strndup(tmp, MAXLEN-1);
4761 g_free(tmp);
4762 } else {
4763 data->file = strndup((char *)key, MAXLEN-1);
4764 }
4765 xmlFree(key);
4766 }
4767 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"voladj")) {
4768 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4769 data->voladj = convf((char *)key);
4770 xmlFree(key);
4771 }
4772 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"duration")) {
4773 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4774 data->duration = convf((char *)key);
4775 xmlFree(key);
4776 }
4777 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"size")) {
4778 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4779 sscanf((char *)key, "%u", &data->size);
4780 xmlFree(key);
4781 }
4782 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"ntracks")) {
4783 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4784 sscanf((char *)key, "%hu", &data->ntracks);
4785 xmlFree(key);
4786 }
4787 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"actrack")) {
4788 if ((key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
4789 sscanf((char *)key, "%hu", &data->actrack);
4790 xmlFree(key);
4791 }
4792 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"active")) {
4793 PL_SET_FLAG(data, PL_FLAG_ACTIVE);
4794 } else if (!xmlStrcmp(cur->name, (const xmlChar *)"cover")) {
4795 PL_SET_FLAG(data, PL_FLAG_COVER);
4796 }
4797 }
4798
4799 if (IS_PL_ALBUM_NODE(data)) {
4800 int act_flag = 0;
4801 if (flags & PL_FLAG_ACTIVE) {
4802 /* ACTIVE flag has been forced by playlist_load_xml_thread */
4803 act_flag = PL_FLAG_ACTIVE;
4804 data->actrack = 1;
4805 for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4806 if (!xmlStrcmp(cur->name, (const xmlChar *)"track")) {
4807 data->ntracks++;
4808 }
4809 }
4810 }
4811
4812 playlist_thread_add_to_list(pt, data);
4813
4814 for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4815 if (!xmlStrcmp(cur->name, (const xmlChar *)"track")) {
4816 parse_playlist_track(pt, doc, cur, PL_FLAG_ALBUM_CHILD | act_flag);
4817 act_flag = 0;
4818 }
4819 }
4820 } else if (data->file != NULL) {
4821 playlist_thread_add_to_list(pt, data);
4822 }
4823 }
4824
4825 void *
playlist_load_xml_thread(void * arg)4826 playlist_load_xml_thread(void * arg) {
4827
4828 playlist_transfer_t * pt = (playlist_transfer_t *)arg;
4829 xmlDocPtr doc = (xmlDocPtr)pt->xml_doc;
4830 xmlNodePtr cur = (xmlNodePtr)pt->xml_node;
4831 int act_flag = 0;
4832
4833 AQUALUNG_THREAD_DETACH();
4834
4835 AQUALUNG_MUTEX_LOCK(pt->pl->thread_mutex);
4836
4837 if (pt->start_playback) {
4838 int has_active;
4839 parse_playlist_status(doc, cur, NULL, NULL, &has_active);
4840 if (!has_active) {
4841 act_flag = PL_FLAG_ACTIVE;
4842 }
4843 }
4844
4845 for (cur = cur->xmlChildrenNode; cur != NULL && !pt->pl->thread_stop; cur = cur->next) {
4846
4847 if (!xmlStrcmp(cur->name, (const xmlChar *)"track")) {
4848 parse_playlist_track(pt, doc, cur, act_flag);
4849 act_flag = 0;
4850 }
4851 if (!xmlStrcmp(cur->name, (const xmlChar *)"record")) {
4852 parse_playlist_track(pt, doc, cur, PL_FLAG_ALBUM_NODE | act_flag);
4853 act_flag = 0;
4854 }
4855 }
4856
4857 playlist_thread_add_to_list(pt, NULL);
4858
4859 AQUALUNG_MUTEX_UNLOCK(pt->pl->thread_mutex);
4860
4861 playlist_transfer_free(pt);
4862
4863 return NULL;
4864 }
4865
4866 void
playlist_load_xml_single(char * filename,playlist_transfer_t * pt)4867 playlist_load_xml_single(char * filename, playlist_transfer_t * pt) {
4868
4869 xmlDocPtr doc;
4870 xmlNodePtr cur;
4871
4872 doc = xmlParseFile(filename);
4873 if (doc == NULL) {
4874 fprintf(stderr, "An XML error occured while parsing %s\n", filename);
4875 return;
4876 }
4877
4878 cur = xmlDocGetRootElement(doc);
4879 if (cur == NULL) {
4880 fprintf(stderr, "empty XML document\n");
4881 xmlFreeDoc(doc);
4882 return;
4883 }
4884
4885 if (xmlStrcmp(cur->name, (const xmlChar *)"aqualung_playlist")) {
4886 fprintf(stderr, "XML document of the wrong type\n");
4887 xmlFreeDoc(doc);
4888 return;
4889 }
4890
4891 if (!pt->pl->name_set || pt->clear) {
4892 xmlChar * pl_name = parse_playlist_name(doc, cur);
4893 if (pl_name != NULL) {
4894 playlist_rename(pt->pl, (char *)pl_name);
4895 xmlFree(pl_name);
4896 }
4897 }
4898
4899 if (playlist_get_playing() == NULL) {
4900 int pl_act;
4901 parse_playlist_status(doc, cur, &pl_act, NULL, NULL);
4902 playlist_set_playing(pt->pl, pl_act);
4903 }
4904
4905 pt->xml_doc = doc;
4906 pt->xml_node = cur;
4907
4908 playlist_progress_bar_show(pt->pl);
4909 AQUALUNG_THREAD_CREATE(pt->pl->thread_id, NULL,
4910 playlist_load_xml_thread, pt);
4911 }
4912
4913 void
playlist_load_xml_multi(char * filename,int start_playback)4914 playlist_load_xml_multi(char * filename, int start_playback) {
4915
4916 xmlDocPtr doc;
4917 xmlNodePtr root;
4918 xmlNodePtr cur;
4919 int * ref = NULL;
4920 int current = -1;
4921 int force_active = start_playback;
4922 int i = gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook));
4923
4924 doc = xmlParseFile(filename);
4925 if (doc == NULL) {
4926 fprintf(stderr, "An XML error occured while parsing %s\n", filename);
4927 return;
4928 }
4929
4930 root = xmlDocGetRootElement(doc);
4931 if (root == NULL) {
4932 fprintf(stderr, "empty XML document\n");
4933 xmlFreeDoc(doc);
4934 return;
4935 }
4936
4937 if (xmlStrcmp(root->name, (const xmlChar *)"aqualung_playlist_tabs")) {
4938 fprintf(stderr, "XML document of the wrong type\n");
4939 xmlFreeDoc(doc);
4940 return;
4941 }
4942
4943 if ((ref = (int *)malloc(sizeof(int *))) == NULL) {
4944 fprintf(stderr, "playlist_load_xml_multi(): malloc error\n");
4945 return;
4946 }
4947
4948 *ref = 1;
4949
4950 if (start_playback) {
4951 for (cur = root->xmlChildrenNode; cur != NULL; cur = cur->next) {
4952 if (!xmlStrcmp(cur->name, (const xmlChar *)"tab")) {
4953 int pl_act;
4954 parse_playlist_status(doc, cur, &pl_act, NULL, NULL);
4955 if (pl_act) {
4956 force_active = 0;
4957 break;
4958 }
4959 }
4960 }
4961 }
4962
4963 for (cur = root->xmlChildrenNode; cur != NULL; cur = cur->next) {
4964
4965 if (!xmlStrcmp(cur->name, (const xmlChar *)"tab")) {
4966
4967 playlist_t * pl = NULL;
4968 playlist_transfer_t * pt = NULL;
4969 xmlChar * pl_name = NULL;
4970 int pl_act, pl_cur;
4971
4972 pl_name = parse_playlist_name(doc, cur);
4973 pl = playlist_tab_new((char *)pl_name);
4974 if (pl_name != NULL) {
4975 xmlFree(pl_name);
4976 }
4977
4978 parse_playlist_status(doc, cur, &pl_act, &pl_cur, NULL);
4979 if (playlist_get_playing() == NULL) {
4980 playlist_set_playing(pl, pl_act);
4981 }
4982
4983 pt = playlist_transfer_new(pl);
4984
4985 pt->xml_doc = doc;
4986 pt->xml_node = cur;
4987 pt->xml_ref = ref;
4988
4989 *ref += 1;
4990
4991 if (pl_act || (pl_cur && force_active)) {
4992 pt->start_playback = start_playback;
4993 start_playback = 0;
4994 force_active = 0;
4995 }
4996 if (pl_cur) {
4997 current = i;
4998 }
4999
5000 ++i;
5001
5002 playlist_progress_bar_show(pt->pl);
5003 AQUALUNG_THREAD_CREATE(pt->pl->thread_id, NULL,
5004 playlist_load_xml_thread, pt);
5005 }
5006 }
5007
5008 *ref -= 1;
5009
5010 gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), current);
5011 }
5012
5013 void *
playlist_load_m3u_thread(void * arg)5014 playlist_load_m3u_thread(void * arg) {
5015
5016 FILE * f;
5017 gint c;
5018 gint i = 0;
5019 gint n;
5020 gchar * str;
5021 gchar line[MAXLEN];
5022 gchar name[MAXLEN];
5023 gchar path[MAXLEN];
5024 gchar tmp[MAXLEN];
5025 gchar pl_dir[MAXLEN];
5026 gint have_name = 0;
5027 gint end_of_file_reached = 0;
5028
5029 playlist_transfer_t * pt = (playlist_transfer_t *)arg;
5030 playlist_data_t * data = NULL;
5031
5032
5033 AQUALUNG_THREAD_DETACH();
5034
5035 AQUALUNG_MUTEX_LOCK(pt->pl->thread_mutex);
5036
5037 if ((f = fopen(pt->filename, "rb")) == NULL) {
5038 fprintf(stderr, "unable to open .m3u playlist: %s\n", pt->filename);
5039 goto finish;
5040 }
5041
5042 str = strrchr(pt->filename, '/');
5043 for (i = 0; (i < (str - pt->filename)) && (i < MAXLEN-1); i++) {
5044 pl_dir[i] = pt->filename[i];
5045 }
5046 pl_dir[i] = '\0';
5047
5048 i = 0;
5049 while (!end_of_file_reached && !pt->pl->thread_stop) {
5050 c = fgetc(f);
5051 if ((c != EOF) && (c != '\n') && (c != '\r') && (i < MAXLEN-1)) {
5052 if ((i > 0) || ((c != ' ') && (c != '\t'))) {
5053 line[i++] = c;
5054 }
5055 } else {
5056 end_of_file_reached = (c == EOF);
5057 line[i] = '\0';
5058 if (i == 0) {
5059 continue;
5060 }
5061 i = 0;
5062
5063 if (strstr(line, "#EXTM3U") == line) {
5064 continue;
5065 }
5066
5067 if (strstr(line, "#EXTINF:") == line) {
5068 int cnt = 0;
5069 while ((line[cnt+8] >= '0') && (line[cnt+8] <= '9') && cnt < 63) {
5070 ++cnt;
5071 }
5072 snprintf(name, MAXLEN-1, "%s", line+cnt+9);
5073 have_name = 1;
5074 } else {
5075 if (!httpc_is_url(line)) {
5076 /* safeguard against C:\ stuff */
5077 if ((line[1] == ':') && (line[2] == '\\')) {
5078 fprintf(stderr, "Ignoring playlist item: %s\n", line);
5079 i = 0;
5080 have_name = 0;
5081 continue;
5082 }
5083
5084 snprintf(path, MAXLEN-1, "%s", line);
5085
5086 /* path curing: turn \-s into /-s */
5087 for (n = 0; n < strlen(path); n++) {
5088 if (path[n] == '\\')
5089 path[n] = '/';
5090 }
5091
5092 if (path[0] != '/') {
5093 strncpy(tmp, path, MAXLEN-1);
5094 snprintf(path, MAXLEN-1, "%s/%s", pl_dir, tmp);
5095 }
5096
5097 if (!have_name) {
5098 gchar * ch;
5099 if ((ch = strrchr(path, '/')) != NULL) {
5100 ++ch;
5101 snprintf(name, MAXLEN-1, "%s", ch);
5102 } else {
5103 fprintf(stderr,
5104 "warning: ain't this a directory? : %s\n", path);
5105 snprintf(name, MAXLEN-1, "%s", path);
5106 }
5107 }
5108
5109 data = playlist_filemeta_get(path);
5110 } else {
5111
5112 data = playlist_filemeta_get(line);
5113 }
5114
5115 if (have_name && data != NULL) {
5116 free_strdup(&data->title, name);
5117 }
5118
5119 have_name = 0;
5120
5121 if (data == NULL) {
5122 fprintf(stderr, "load_m3u(): playlist_filemeta_get() returned NULL\n");
5123 } else {
5124 playlist_thread_add_to_list(pt, data);
5125 }
5126 }
5127 }
5128 }
5129
5130 playlist_thread_add_to_list(pt, NULL);
5131
5132 finish:
5133
5134 AQUALUNG_MUTEX_UNLOCK(pt->pl->thread_mutex);
5135
5136 playlist_transfer_free(pt);
5137
5138 return NULL;
5139 }
5140
5141
5142 void
load_pls_load(playlist_transfer_t * pt,char * file,char * title,gint * have_file,gint * have_title)5143 load_pls_load(playlist_transfer_t * pt, char * file, char * title, gint * have_file, gint * have_title) {
5144
5145 playlist_data_t * data = NULL;
5146
5147 if (*have_file == 0) {
5148 return;
5149 }
5150
5151 data = playlist_filemeta_get(file);
5152
5153 if (data && *have_title) {
5154 free_strdup(&data->title, title);
5155 }
5156
5157 if (data == NULL) {
5158 fprintf(stderr, "load_pls_load(): playlist_filemeta_get() returned NULL\n");
5159 } else {
5160 playlist_thread_add_to_list(pt, data);
5161 }
5162
5163 *have_file = *have_title = 0;
5164 }
5165
5166 void *
playlist_load_pls_thread(void * arg)5167 playlist_load_pls_thread(void * arg) {
5168
5169 FILE * f;
5170 gint c;
5171 gint i = 0;
5172 gint n;
5173 gchar * str;
5174 gchar line[MAXLEN];
5175 gchar file[MAXLEN];
5176 gchar title[MAXLEN];
5177 gchar tmp[MAXLEN];
5178 gchar pl_dir[MAXLEN];
5179 gint have_file = 0;
5180 gint have_title = 0;
5181 gchar numstr_file[10];
5182 gchar numstr_title[10];
5183 gint end_of_file_reached = 0;
5184
5185 playlist_transfer_t * pt = (playlist_transfer_t *)arg;
5186
5187
5188 AQUALUNG_THREAD_DETACH();
5189
5190 AQUALUNG_MUTEX_LOCK(pt->pl->thread_mutex);
5191
5192 if ((f = fopen(pt->filename, "rb")) == NULL) {
5193 fprintf(stderr, "unable to open .pls playlist: %s\n", pt->filename);
5194 goto finish;
5195 }
5196
5197 str = strrchr(pt->filename, '/');
5198 for (i = 0; (i < (str - pt->filename)) && (i < MAXLEN-1); i++) {
5199 pl_dir[i] = pt->filename[i];
5200 }
5201 pl_dir[i] = '\0';
5202
5203 i = 0;
5204 while (!end_of_file_reached && !pt->pl->thread_stop) {
5205 c = fgetc(f);
5206 if ((c != EOF) && (c != '\n') && (c != '\r') && (i < MAXLEN-1)) {
5207 if ((i > 0) || ((c != ' ') && (c != '\t'))) {
5208 line[i++] = c;
5209 }
5210 } else {
5211 end_of_file_reached = (c == EOF);
5212 line[i] = '\0';
5213 if (i == 0)
5214 continue;
5215 i = 0;
5216
5217 if (strstr(line, "[playlist]") == line) {
5218 continue;
5219 }
5220 if (strcasestr(line, "NumberOfEntries") == line) {
5221 continue;
5222 }
5223 if (strstr(line, "Version") == line) {
5224 continue;
5225 }
5226
5227 if (strstr(line, "File") == line) {
5228
5229 char numstr[10];
5230 char * ch;
5231 int m;
5232
5233 if (have_file) {
5234 load_pls_load(pt, file, title, &have_file, &have_title);
5235 }
5236
5237 if ((ch = strstr(line, "=")) == NULL) {
5238 fprintf(stderr, "Syntax error in %s\n", pt->filename);
5239 goto finish;
5240 }
5241
5242 ++ch;
5243 while ((*ch == ' ') || (*ch == '\t'))
5244 ++ch;
5245
5246 if (!httpc_is_url(ch)) {
5247
5248 m = 0;
5249 for (n = 0; (line[n+4] != '=') && (m < sizeof(numstr)); n++) {
5250 if ((line[n+4] != ' ') && (line[n+4] != '\t'))
5251 numstr[m++] = line[n+4];
5252 }
5253 numstr[m] = '\0';
5254 strncpy(numstr_file, numstr, sizeof(numstr_file));
5255
5256 /* safeguard against C:\ stuff */
5257 if ((ch[1] == ':') && (ch[2] == '\\')) {
5258 fprintf(stderr, "Ignoring playlist item: %s\n", ch);
5259 i = 0;
5260 have_file = have_title = 0;
5261 continue;
5262 }
5263
5264 snprintf(file, MAXLEN-1, "%s", ch);
5265
5266 /* path curing: turn \-s into /-s */
5267
5268 for (n = 0; n < strlen(file); n++) {
5269 if (file[n] == '\\')
5270 file[n] = '/';
5271 }
5272
5273 if (file[0] != '/') {
5274 strncpy(tmp, file, MAXLEN-1);
5275 snprintf(file, MAXLEN-1, "%s/%s", pl_dir, tmp);
5276 }
5277
5278 } else {
5279 snprintf(file, MAXLEN-1, "%s", ch);
5280 }
5281
5282 have_file = 1;
5283
5284 } else if (strstr(line, "Title") == line) {
5285
5286 char numstr[10];
5287 char * ch;
5288 int m;
5289
5290 if ((ch = strstr(line, "=")) == NULL) {
5291 fprintf(stderr, "Syntax error in %s\n", pt->filename);
5292 goto finish;
5293 }
5294
5295 ++ch;
5296 while ((*ch == ' ') || (*ch == '\t'))
5297 ++ch;
5298
5299 m = 0;
5300 for (n = 0; (line[n+5] != '=') && (m < sizeof(numstr)); n++) {
5301 if ((line[n+5] != ' ') && (line[n+5] != '\t'))
5302 numstr[m++] = line[n+5];
5303 }
5304 numstr[m] = '\0';
5305 strncpy(numstr_title, numstr, sizeof(numstr_title));
5306
5307 snprintf(title, MAXLEN-1, "%s", ch);
5308 have_title = 1;
5309
5310
5311 } else if (strstr(line, "Length") == line) {
5312
5313 /* We parse the timing, but throw it away.
5314 This may change in the future. */
5315
5316 char * ch;
5317 if ((ch = strstr(line, "=")) == NULL) {
5318 fprintf(stderr, "Syntax error in %s\n", pt->filename);
5319 goto finish;
5320 }
5321
5322 ++ch;
5323 while ((*ch == ' ') || (*ch == '\t'))
5324 ++ch;
5325
5326 } else {
5327 fprintf(stderr,
5328 "Syntax error: invalid line in playlist: %s\n", line);
5329 goto finish;
5330 }
5331
5332 if (!have_file || !have_title) {
5333 continue;
5334 }
5335
5336 if (strcmp(numstr_file, numstr_title) != 0) {
5337 continue;
5338 }
5339
5340 load_pls_load(pt, file, title, &have_file, &have_title);
5341 }
5342 }
5343
5344 load_pls_load(pt, file, title, &have_file, &have_title);
5345
5346 playlist_thread_add_to_list(pt, NULL);
5347
5348 finish:
5349
5350 AQUALUNG_MUTEX_UNLOCK(pt->pl->thread_mutex);
5351
5352 playlist_transfer_free(pt);
5353
5354 return NULL;
5355 }
5356
5357
5358 int
playlist_get_type(char * filename)5359 playlist_get_type(char * filename) {
5360
5361 FILE * f;
5362 gchar buf1[] = "<?xml version=\"1.0\"?>\n<aqualung_playlist_tabs"; /* this must be the 1st */
5363 gchar buf2[] = "<?xml version=\"1.0\"?>\n<aqualung_playlist"; /* this must be the 2nd */
5364 gchar inbuf[48];
5365 gint len;
5366
5367 if ((f = fopen(filename, "rb")) == NULL) {
5368 return 0;
5369 }
5370
5371 len = fread(inbuf, 1, 47, f);
5372 fclose(f);
5373
5374 inbuf[len] = '\0';
5375
5376 if (strstr(inbuf, buf1) == inbuf) {
5377 return PLAYLIST_XML_MULTI;
5378 }
5379
5380 if (strstr(inbuf, buf2) == inbuf) {
5381 return PLAYLIST_XML_SINGLE;
5382 }
5383
5384 /* not Aqualung playlist, decide by file extension */
5385
5386 len = strlen(filename);
5387 if (len < 5) {
5388 return PLAYLIST_INVALID;
5389 }
5390
5391 if ((filename[len-4] == '.') &&
5392 ((filename[len-3] == 'm') || (filename[len-3] == 'M')) &&
5393 (filename[len-2] == '3') &&
5394 ((filename[len-1] == 'u') || (filename[len-1] == 'U'))) {
5395 return PLAYLIST_M3U;
5396 }
5397
5398 if ((filename[len-4] == '.') &&
5399 ((filename[len-3] == 'p') || (filename[len-3] == 'P')) &&
5400 ((filename[len-2] == 'l') || (filename[len-2] == 'L')) &&
5401 ((filename[len-1] == 's') || (filename[len-1] == 'S'))) {
5402 return PLAYLIST_PLS;
5403 }
5404
5405 return PLAYLIST_INVALID;
5406 }
5407
5408
5409 void
init_plist_menu(GtkWidget * append_menu)5410 init_plist_menu(GtkWidget *append_menu) {
5411
5412 GtkWidget * separator;
5413
5414 plist__tab_new = gtk_menu_item_new_with_label(_("New tab"));
5415 plist__save = gtk_menu_item_new_with_label(_("Save playlist"));
5416 plist__save_all = gtk_menu_item_new_with_label(_("Save all playlists"));
5417 plist__load_tab = gtk_menu_item_new_with_label(_("Load playlist in new tab"));
5418 plist__load = gtk_menu_item_new_with_label(_("Load playlist"));
5419 plist__enqueue = gtk_menu_item_new_with_label(_("Enqueue playlist"));
5420 #ifdef HAVE_IFP
5421 plist__send_songs_to_iriver = gtk_menu_item_new_with_label(_("Send to iFP device"));
5422 #endif /* HAVE_IFP */
5423 #ifdef HAVE_TRANSCODING
5424 plist__export = gtk_menu_item_new_with_label(_("Export files"));
5425 #endif /* HAVE_TRANSCODING */
5426 plist__rva = gtk_menu_item_new_with_label(_("Calculate RVA"));
5427 plist__rva_menu = gtk_menu_new();
5428 plist__rva_separate = gtk_menu_item_new_with_label(_("Separate"));
5429 plist__rva_average = gtk_menu_item_new_with_label(_("Average"));
5430 plist__reread_file_meta = gtk_menu_item_new_with_label(_("Reread file metadata"));
5431 plist__fileinfo = gtk_menu_item_new_with_label(_("File info..."));
5432 plist__search = gtk_menu_item_new_with_label(_("Search..."));
5433 plist__roll = gtk_menu_item_new_with_label(_("Roll to active song"));
5434
5435 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__tab_new);
5436
5437 separator = gtk_separator_menu_item_new();
5438 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), separator);
5439 gtk_widget_show(separator);
5440
5441 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__save);
5442 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__save_all);
5443
5444 separator = gtk_separator_menu_item_new();
5445 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), separator);
5446 gtk_widget_show(separator);
5447
5448 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__load);
5449 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__load_tab);
5450 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__enqueue);
5451
5452 separator = gtk_separator_menu_item_new();
5453 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), separator);
5454 gtk_widget_show(separator);
5455
5456 #ifdef HAVE_IFP
5457 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__send_songs_to_iriver);
5458 #endif /* HAVE_IFP */
5459 #ifdef HAVE_TRANSCODING
5460 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__export);
5461 #endif /* HAVE_TRANSCODING */
5462
5463 separator = gtk_separator_menu_item_new();
5464 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), separator);
5465 gtk_widget_show(separator);
5466
5467 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__rva);
5468 gtk_menu_item_set_submenu(GTK_MENU_ITEM(plist__rva), plist__rva_menu);
5469 gtk_menu_shell_append(GTK_MENU_SHELL(plist__rva_menu), plist__rva_separate);
5470 gtk_menu_shell_append(GTK_MENU_SHELL(plist__rva_menu), plist__rva_average);
5471 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__reread_file_meta);
5472
5473 separator = gtk_separator_menu_item_new();
5474 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), separator);
5475 gtk_widget_show(separator);
5476
5477 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__fileinfo);
5478 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__search);
5479 gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__roll);
5480
5481 add_custom_command_menu_to_playlist_menu(append_menu);
5482
5483 g_signal_connect_swapped(G_OBJECT(plist__tab_new), "activate", G_CALLBACK(tab__new_cb), NULL);
5484 g_signal_connect_swapped(G_OBJECT(plist__save), "activate", G_CALLBACK(plist__save_cb), NULL);
5485 g_signal_connect_swapped(G_OBJECT(plist__save_all), "activate", G_CALLBACK(plist__save_all_cb), NULL);
5486 g_signal_connect_swapped(G_OBJECT(plist__load_tab), "activate", G_CALLBACK(plist__load_tab_cb), NULL);
5487 g_signal_connect_swapped(G_OBJECT(plist__load), "activate", G_CALLBACK(plist__load_cb), NULL);
5488 g_signal_connect_swapped(G_OBJECT(plist__enqueue), "activate", G_CALLBACK(plist__enqueue_cb), NULL);
5489 g_signal_connect_swapped(G_OBJECT(plist__rva_separate), "activate", G_CALLBACK(plist__rva_separate_cb), NULL);
5490 g_signal_connect_swapped(G_OBJECT(plist__rva_average), "activate", G_CALLBACK(plist__rva_average_cb), NULL);
5491 g_signal_connect_swapped(G_OBJECT(plist__reread_file_meta), "activate", G_CALLBACK(plist__reread_file_meta_cb), NULL);
5492
5493 #ifdef HAVE_IFP
5494 g_signal_connect_swapped(G_OBJECT(plist__send_songs_to_iriver), "activate", G_CALLBACK(plist__send_songs_to_iriver_cb), NULL);
5495 #endif /* HAVE_IFP */
5496 #ifdef HAVE_TRANSCODING
5497 g_signal_connect_swapped(G_OBJECT(plist__export), "activate", G_CALLBACK(plist__export_cb), NULL);
5498 #endif /* HAVE_TRANSCODING */
5499
5500 g_signal_connect_swapped(G_OBJECT(plist__fileinfo), "activate", G_CALLBACK(plist__fileinfo_cb), NULL);
5501 g_signal_connect_swapped(G_OBJECT(plist__search), "activate", G_CALLBACK(plist__search_cb), NULL);
5502 g_signal_connect_swapped(G_OBJECT(plist__roll), "activate", G_CALLBACK(plist__roll_cb), NULL);
5503
5504 gtk_widget_show(plist__tab_new);
5505 gtk_widget_show(plist__save);
5506 gtk_widget_show(plist__save_all);
5507 gtk_widget_show(plist__load_tab);
5508 gtk_widget_show(plist__load);
5509 gtk_widget_show(plist__enqueue);
5510 gtk_widget_show(plist__rva);
5511 gtk_widget_show(plist__rva_separate);
5512 gtk_widget_show(plist__rva_average);
5513 gtk_widget_show(plist__reread_file_meta);
5514 #ifdef HAVE_IFP
5515 gtk_widget_show(plist__send_songs_to_iriver);
5516 #endif /* HAVE_IFP */
5517 #ifdef HAVE_TRANSCODING
5518 gtk_widget_show(plist__export);
5519 #endif /* HAVE_TRANSCODING */
5520 gtk_widget_show(plist__fileinfo);
5521 gtk_widget_show(plist__search);
5522 gtk_widget_show(plist__roll);
5523 }
5524
5525
5526 void
show_active_position_in_playlist(playlist_t * pl)5527 show_active_position_in_playlist(playlist_t * pl) {
5528
5529 GtkTreeIter iter;
5530 playlist_data_t * data;
5531 int flag = 0;
5532
5533 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pl->store), &iter)) {
5534
5535 do {
5536 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
5537 if (IS_PL_ACTIVE(data)) {
5538 flag = 1;
5539 break;
5540 }
5541 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(pl->store), &iter));
5542
5543 if (!flag) {
5544 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, 0);
5545 }
5546
5547 set_cursor_in_playlist(pl, &iter, TRUE);
5548 }
5549 }
5550
5551 void
show_active_position_in_playlist_toggle(playlist_t * pl)5552 show_active_position_in_playlist_toggle(playlist_t * pl) {
5553
5554 GtkTreeIter iter, iter_child;
5555 playlist_data_t * data;
5556 int flag, cflag, j;
5557
5558
5559 flag = cflag = 0;
5560
5561 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pl->store), &iter)) {
5562
5563 do {
5564 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
5565 if (IS_PL_ACTIVE(data)) {
5566 flag = 1;
5567 if (gtk_tree_selection_iter_is_selected(pl->select, &iter) &&
5568 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter)) {
5569
5570 GtkTreePath * visible_path;
5571
5572 gtk_tree_view_get_cursor(GTK_TREE_VIEW(pl->view), &visible_path, NULL);
5573
5574 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(pl->view), visible_path)) {
5575 gtk_tree_view_expand_row(GTK_TREE_VIEW(pl->view), visible_path, FALSE);
5576 }
5577
5578 gtk_tree_path_free(visible_path);
5579
5580 j = 0;
5581 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_child, &iter, j++)) {
5582 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter_child,
5583 PL_COL_DATA, &data, -1);
5584 if (IS_PL_ACTIVE(data)) {
5585 cflag = 1;
5586 break;
5587 }
5588 }
5589 }
5590 break;
5591 }
5592 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(pl->store), &iter));
5593
5594 if (flag) {
5595 if (cflag) {
5596 set_cursor_in_playlist(pl, &iter_child, TRUE);
5597 } else {
5598 set_cursor_in_playlist(pl, &iter, TRUE);
5599 }
5600 }
5601 }
5602 }
5603
5604
5605 void
expand_collapse_album_node(playlist_t * pl)5606 expand_collapse_album_node(playlist_t * pl) {
5607
5608 GtkTreeIter iter, iter_child;
5609 gint i, j;
5610 GtkTreePath *path;
5611
5612 i = 0;
5613 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter, NULL, i++)) {
5614
5615 if (gtk_tree_selection_iter_is_selected(pl->select, &iter) &&
5616 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pl->store), &iter)) {
5617
5618 gtk_tree_view_get_cursor(GTK_TREE_VIEW(pl->view), &path, NULL);
5619
5620 if (path && gtk_tree_view_row_expanded(GTK_TREE_VIEW(pl->view), path)) {
5621 gtk_tree_view_collapse_row(GTK_TREE_VIEW(pl->view), path);
5622 } else {
5623 gtk_tree_view_expand_row(GTK_TREE_VIEW(pl->view), path, FALSE);
5624 }
5625
5626 continue;
5627 }
5628
5629 j = 0;
5630 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pl->store), &iter_child, &iter, j++)) {
5631
5632 if (gtk_tree_selection_iter_is_selected(pl->select, &iter_child) &&
5633 gtk_tree_model_iter_parent(GTK_TREE_MODEL(pl->store), &iter, &iter_child)) {
5634
5635 path = gtk_tree_model_get_path (GTK_TREE_MODEL(pl->store), &iter);
5636
5637 if (path) {
5638 gtk_tree_view_collapse_row(GTK_TREE_VIEW(pl->view), path);
5639 gtk_tree_view_set_cursor(GTK_TREE_VIEW(pl->view), path, NULL, FALSE);
5640 }
5641 }
5642 }
5643 }
5644 }
5645
5646
5647 static gboolean
pl_progress_bar_update(gpointer data)5648 pl_progress_bar_update(gpointer data) {
5649
5650 playlist_t * pl = (playlist_t *)data;
5651
5652 if (pl->progbar != NULL) {
5653 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pl->progbar));
5654 return TRUE;
5655 }
5656
5657 return FALSE;
5658 }
5659
5660 static void
pl_progress_bar_stop_clicked(GtkWidget * widget,gpointer data)5661 pl_progress_bar_stop_clicked(GtkWidget * widget, gpointer data) {
5662
5663 playlist_t * pl = (playlist_t *)data;
5664
5665 pl->thread_stop = 1;
5666 }
5667
5668 void
playlist_progress_bar_show(playlist_t * pl)5669 playlist_progress_bar_show(playlist_t * pl) {
5670
5671 pl->progbar_semaphore++;
5672
5673 if (pl->progbar != NULL) {
5674 return;
5675 }
5676
5677 pl->thread_stop = 0;
5678
5679 playlist_stats_set_busy();
5680
5681 pl->progbar = gtk_progress_bar_new();
5682 gtk_box_pack_start(GTK_BOX(pl->progbar_container), pl->progbar, TRUE, TRUE, 0);
5683
5684 pl->progbar_stop_button = gtk_button_new_with_label(_("Stop adding files"));
5685 gtk_box_pack_start(GTK_BOX(pl->progbar_container), pl->progbar_stop_button, FALSE, FALSE, 0);
5686 g_signal_connect(G_OBJECT(pl->progbar_stop_button), "clicked",
5687 G_CALLBACK(pl_progress_bar_stop_clicked), pl);
5688
5689 gtk_widget_show_all(pl->progbar_container);
5690
5691 aqualung_timeout_add(200, pl_progress_bar_update, pl);
5692 }
5693
5694
5695 void
playlist_progress_bar_hide(playlist_t * pl)5696 playlist_progress_bar_hide(playlist_t * pl) {
5697
5698 if (pl->progbar != NULL) {
5699 gtk_widget_destroy(pl->progbar);
5700 pl->progbar = NULL;
5701 }
5702
5703 if (pl->progbar_stop_button != NULL) {
5704 gtk_widget_destroy(pl->progbar_stop_button);
5705 pl->progbar_stop_button = NULL;
5706 }
5707 }
5708
5709 void
playlist_progress_bar_hide_all()5710 playlist_progress_bar_hide_all() {
5711
5712 GList * list;
5713
5714 for (list = playlists; list; list = list->next) {
5715 playlist_progress_bar_hide((playlist_t *)list->data);
5716 }
5717 }
5718
5719
5720 int
playlist_unlink_files_foreach(playlist_t * pl,GtkTreeIter * iter,void * data)5721 playlist_unlink_files_foreach(playlist_t * pl, GtkTreeIter * iter, void * data) {
5722
5723 playlist_data_t * pldata;
5724
5725 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &pldata, -1);
5726
5727 if (g_file_test(pldata->file, G_FILE_TEST_EXISTS)) {
5728
5729 GtkListStore * store = (GtkListStore *)data;
5730 GtkTreeIter i;
5731
5732 gtk_list_store_append(store, &i);
5733 gtk_list_store_set(store, &i, 0, pldata->file, 1, gtk_tree_iter_copy(iter), -1);
5734 }
5735
5736 return 0;
5737 }
5738
5739 void
playlist_unlink_files(playlist_t * pl)5740 playlist_unlink_files(playlist_t * pl) {
5741
5742 GtkWidget * view;
5743 GtkWidget * scrwin;
5744 GtkWidget * viewport;
5745 GtkCellRenderer * cell;
5746 GtkTreeViewColumn * column;
5747 GtkListStore * store;
5748 GtkTreeIter iter;
5749 int res;
5750 int i;
5751 int error = 0;
5752
5753
5754 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
5755
5756 playlist_foreach_selected(pl, playlist_unlink_files_foreach, store);
5757
5758 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL) == 0) {
5759 return;
5760 }
5761
5762 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
5763 cell = gtk_cell_renderer_text_new();
5764 column = gtk_tree_view_column_new_with_attributes(_("Files selected for removal"), cell, "text", 0, NULL);
5765 gtk_tree_view_append_column(GTK_TREE_VIEW(view), GTK_TREE_VIEW_COLUMN(column));
5766
5767 viewport = gtk_viewport_new(NULL, NULL);
5768 scrwin = gtk_scrolled_window_new(NULL, NULL);
5769 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
5770 gtk_widget_set_size_request(scrwin, -1, 200);
5771
5772 gtk_container_add(GTK_CONTAINER(viewport), scrwin);
5773 gtk_container_add(GTK_CONTAINER(scrwin), view);
5774
5775 res = message_dialog(_("Remove files"),
5776 options.playlist_is_embedded ? main_window : playlist_window,
5777 GTK_MESSAGE_QUESTION,
5778 GTK_BUTTONS_YES_NO,
5779 viewport,
5780 _("The selected files will be deleted from the filesystem. "
5781 "No recovery will be possible after this operation.\n\n"
5782 "Do you want to proceed?"));
5783
5784 i = 0;
5785 while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, i++)) {
5786
5787 GtkTreeIter * pl_iter;
5788
5789 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 1, &pl_iter, -1);
5790
5791 if (res == GTK_RESPONSE_YES) {
5792
5793 gchar * file;
5794 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &file, -1);
5795
5796 if (unlink(file) != 0) {
5797 fprintf(stderr, "unlink: unable to unlink %s\n", file);
5798 perror("unlink");
5799 gtk_tree_selection_unselect_iter(pl->select, pl_iter);
5800 ++error;
5801 }
5802
5803 g_free(file);
5804 }
5805
5806 gtk_tree_iter_free(pl_iter);
5807 }
5808
5809 if (res == GTK_RESPONSE_YES) {
5810 rem__sel_cb(pl);
5811 }
5812
5813 if (error) {
5814 message_dialog(_("Remove files"),
5815 options.playlist_is_embedded ? main_window : playlist_window,
5816 GTK_MESSAGE_WARNING,
5817 GTK_BUTTONS_OK,
5818 NULL,
5819 (error == 1) ? _("Unable to remove %d file.") : _("Unable to remove %d files."),
5820 error);
5821 }
5822 }
5823
5824
5825 void
set_cursor_in_playlist(playlist_t * pl,GtkTreeIter * iter,gboolean scroll)5826 set_cursor_in_playlist(playlist_t * pl, GtkTreeIter *iter, gboolean scroll) {
5827
5828 GtkTreePath * visible_path;
5829
5830 visible_path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl->store), iter);
5831
5832 if (visible_path) {
5833 gtk_tree_view_set_cursor(GTK_TREE_VIEW(pl->view), visible_path, NULL, FALSE);
5834 if (scroll == TRUE) {
5835 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW (pl->view), visible_path,
5836 NULL, TRUE, 0.5, 0.0);
5837 }
5838 gtk_tree_path_free(visible_path);
5839 }
5840 }
5841
5842 void
select_active_position_in_playlist(playlist_t * pl)5843 select_active_position_in_playlist(playlist_t * pl) {
5844
5845 GtkTreeIter iter;
5846 playlist_data_t * data;
5847
5848 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pl->store), &iter)) {
5849
5850 do {
5851 gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);
5852 if (IS_PL_ACTIVE(data)) {
5853 set_cursor_in_playlist(pl, &iter, FALSE);
5854 break;
5855 }
5856 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(pl->store), &iter));
5857 }
5858 }
5859
5860 void
roll_to_active_track(playlist_t * pl,GtkTreeIter * piter)5861 roll_to_active_track(playlist_t * pl, GtkTreeIter * piter) {
5862 GtkTreePath * active_path;
5863
5864 active_path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl->store), piter);
5865
5866 if (active_path) {
5867 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(pl->view), active_path);
5868 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(pl->view), active_path, NULL, TRUE, 0.5, 0.0);
5869 }
5870 gtk_tree_path_free(active_path);
5871 }
5872
5873 /* passed as fileinfo_model_func_t argument to show_file_info */
5874 gboolean
playlist_model_func(GtkTreeModel * model,GtkTreeIter iter,char ** name,char ** file)5875 playlist_model_func(GtkTreeModel * model, GtkTreeIter iter, char**name, char**file) {
5876 playlist_data_t * data;
5877 gtk_tree_model_get(model, &iter, PL_COL_NAME, name, -1);
5878 gtk_tree_model_get(model, &iter, PL_COL_DATA, &data, -1);
5879 *file = strdup(data->file);
5880 return TRUE;
5881 }
5882
5883 // vim: shiftwidth=8:tabstop=8:softtabstop=8 :
5884
5885