2
Copyright (C) 2004 Tom Szilagyi
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.
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.
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.
18
$Id: playlist.c 585 2007-02-12 11:18:05Z peterszilagyi $
31
#include <gdk/gdkkeysyms.h>
33
#include <glib/gstdio.h>
34
#include <libxml/xmlmemory.h>
35
#include <libxml/parser.h>
42
#include "music_browser.h"
43
#include "file_info.h"
44
#include "decoder/dec_mod.h"
45
#include "decoder/file_decoder.h"
46
#include "meta_decoder.h"
50
#include "search_playlist.h"
52
#include "ifp_device.h"
54
extern options_t options;
56
extern void assign_audio_fc_filters(GtkFileChooser *fc);
57
extern void assign_playlist_fc_filters(GtkFileChooser *fc);
59
extern char pl_color_active[14];
60
extern char pl_color_inactive[14];
62
extern GtkTooltips * aqualung_tooltips;
64
extern GtkWidget* gui_stock_label_button(gchar *blabel, const gchar *bstock);
66
extern PangoFontDescription *fd_playlist;
67
extern PangoFontDescription *fd_statusbar;
69
GtkWidget * playlist_window;
70
GtkWidget * da_dialog;
71
GtkWidget * scrolled_win;
73
extern GtkWidget * main_window;
74
extern GtkWidget * info_window;
75
extern GtkWidget * vol_window;
77
extern GtkTreeSelection * music_select;
79
gint playlist_color_is_set;
82
gint playlist_scroll_up_tag = -1;
83
gint playlist_scroll_dn_tag = -1;
86
extern gulong play_id;
87
extern gulong pause_id;
88
extern GtkWidget * play_button;
89
extern GtkWidget * pause_button;
91
extern int vol_finished;
95
vol_queue_t * pl_vol_queue;
97
/* used to store array of tree iters of tracks selected for RVA calc */
98
GtkTreeIter * vol_iters;
101
GtkWidget * play_list;
102
GtkTreeStore * play_store = 0;
103
GtkTreeSelection * play_select;
104
GtkTreeViewColumn * track_column;
105
GtkTreeViewColumn * rva_column;
106
GtkTreeViewColumn * length_column;
107
GtkCellRenderer * track_renderer;
108
GtkCellRenderer * rva_renderer;
109
GtkCellRenderer * length_renderer;
111
GtkWidget * statusbar_total;
112
GtkWidget * statusbar_total_label;
113
GtkWidget * statusbar_selected;
114
GtkWidget * statusbar_selected_label;
116
volatile int playlist_thread_stop;
117
int pl_progress_bar_semaphore;
119
GtkWidget * pl_progress_bar;
120
GtkWidget * pl_progress_bar_container;
121
GtkWidget * pl_progress_bar_stop_button;
123
volatile int playlist_data_written;
125
AQUALUNG_THREAD_DECLARE(playlist_thread_id)
126
AQUALUNG_MUTEX_DECLARE(playlist_thread_mutex)
127
AQUALUNG_MUTEX_DECLARE(playlist_wait_mutex)
128
AQUALUNG_COND_DECLARE_INIT(playlist_thread_wait)
132
GtkWidget * add_menu;
133
GtkWidget * add__files;
134
GtkWidget * add__dir;
135
GtkWidget * sel_menu;
136
GtkWidget * sel__none;
137
GtkWidget * sel__all;
138
GtkWidget * sel__inv;
139
GtkWidget * rem_menu;
140
GtkWidget * cut__sel;
141
GtkWidget * rem__all;
142
GtkWidget * rem__dead;
143
GtkWidget * rem__sel;
144
GtkWidget * plist_menu;
145
GtkWidget * plist__save;
146
GtkWidget * plist__load;
147
GtkWidget * plist__enqueue;
148
GtkWidget * plist__separator1;
149
GtkWidget * plist__rva;
150
GtkWidget * plist__rva_menu;
151
GtkWidget * plist__rva_separate;
152
GtkWidget * plist__rva_average;
153
GtkWidget * plist__reread_file_meta;
154
GtkWidget * plist__separator2;
155
GtkWidget * plist__fileinfo;
156
GtkWidget * plist__search;
159
GtkWidget * plist__send_songs_to_iriver;
160
GtkWidget * plist__separator3;
161
#endif /* HAVE_IFP */
163
gchar command[RB_CONTROL_SIZE];
165
gchar fileinfo_name[MAXLEN];
166
gchar fileinfo_file[MAXLEN];
168
extern int is_file_loaded;
169
extern int is_paused;
170
extern int allow_seeks;
172
extern char current_file[MAXLEN];
174
extern rb_t * rb_gui2disk;
176
extern GtkWidget * playlist_toggle;
179
typedef struct _playlist_filemeta {
189
playlist_filemeta * playlist_filemeta_get(char * physical_name, char * alt_name, int composit);
190
void playlist_filemeta_free(playlist_filemeta * plfm);
192
void remove_files (void);
193
void set_cursor_in_playlist (GtkTreeIter *iter, gboolean scroll);
194
void select_active_position_in_playlist(void);
196
void playlist_selection_changed(GtkTreeSelection * sel, gpointer data);
198
void sel__all_cb(gpointer data);
199
void sel__none_cb(gpointer data);
200
void rem__all_cb(gpointer data);
201
void rem__sel_cb(gpointer data);
202
void cut__sel_cb(gpointer data);
203
void plist__search_cb(gpointer data);
204
void add_files(GtkWidget * widget, gpointer data);
205
void recalc_album_node(GtkTreeIter * iter);
207
void add_directory(GtkWidget * widget, gpointer data);
208
void init_plist_menu(GtkWidget *append_menu);
209
void show_active_position_in_playlist(void);
210
void show_active_position_in_playlist_toggle(void);
211
void show_last_position_in_playlist(void);
212
void expand_collapse_album_node(void);
214
void load_playlist(char * filename, int enqueue);
215
void load_m3u(char * filename, int enqueue);
216
void load_pls(char * filename, int enqueue);
217
gint is_playlist(char * filename);
222
voladj2str(float voladj, char * str) {
224
if (fabs(voladj) < 0.05f) {
225
sprintf(str, " %.1f dB", 0.0f);
227
if (voladj >= 0.0f) {
228
sprintf(str, " %.1f dB", voladj);
230
sprintf(str, "%.1f dB", voladj);
236
disable_bold_font_in_playlist() {
241
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
243
GtkTreeIter iter_child;
245
gtk_tree_store_set(play_store, &iter, COLUMN_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, -1);
246
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, &iter, j++)) {
247
gtk_tree_store_set(play_store, &iter_child, COLUMN_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, -1);
254
adjust_playlist_item_color(GtkTreeIter * piter, char * active, char * inactive) {
258
gtk_tree_model_get(GTK_TREE_MODEL(play_store), piter, COLUMN_SELECTION_COLOR, &str, -1);
260
if (strcmp(str, pl_color_active) == 0) {
261
gtk_tree_store_set(play_store, piter, COLUMN_SELECTION_COLOR, active, -1);
262
if (options.show_active_track_name_in_bold) {
263
gtk_tree_store_set(play_store, piter, COLUMN_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
267
if (strcmp(str, pl_color_inactive) == 0) {
268
gtk_tree_store_set(play_store, piter,
269
COLUMN_SELECTION_COLOR, inactive,
270
COLUMN_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, -1);
278
set_playlist_color() {
286
if (options.override_skin_settings && (gdk_color_parse(options.activesong_color, &color) == TRUE)) {
288
if (!color.red && !color.green && !color.blue) {
292
play_list->style->fg[SELECTED].red = color.red;
293
play_list->style->fg[SELECTED].green = color.green;
294
play_list->style->fg[SELECTED].blue = color.blue;
297
sprintf(active, "#%04X%04X%04X",
298
play_list->style->fg[SELECTED].red,
299
play_list->style->fg[SELECTED].green,
300
play_list->style->fg[SELECTED].blue);
302
sprintf(inactive, "#%04X%04X%04X",
303
play_list->style->fg[INSENSITIVE].red,
304
play_list->style->fg[INSENSITIVE].green,
305
play_list->style->fg[INSENSITIVE].blue);
307
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
309
GtkTreeIter iter_child;
311
adjust_playlist_item_color(&iter, active, inactive);
312
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, &iter, j++)) {
313
adjust_playlist_item_color(&iter_child, active, inactive);
317
strcpy(pl_color_active, active);
318
strcpy(pl_color_inactive, inactive);
322
/* Calls foreach on each selected track iter. A track is selected iff
323
it is selected or its parent album node is selected. */
326
playlist_foreach_selected(void (* foreach)(GtkTreeIter *, void *), void * data) {
328
GtkTreeIter iter_top;
334
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_top, NULL, i++)) {
336
gboolean topsel = gtk_tree_selection_iter_is_selected(play_select, &iter_top);
338
if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(play_store), &iter_top)) {
341
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, &iter_top, j++)) {
342
if (topsel || gtk_tree_selection_iter_is_selected(play_select, &iter)) {
343
(*foreach)(&iter, data);
348
(*foreach)(&iter_top, data);
355
get_playing_path(void) {
362
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
364
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_SELECTION_COLOR, &color, -1);
366
if (strcmp(color, pl_color_active) == 0) {
367
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) > 0) {
369
GtkTreeIter iter_child;
373
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store),
374
&iter_child, &iter, j++)) {
376
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter_child,
377
COLUMN_SELECTION_COLOR, &color_child, -1);
379
if (strcmp(color_child, pl_color_active) == 0) {
382
return gtk_tree_model_get_path(GTK_TREE_MODEL(play_store), &iter_child);
389
return gtk_tree_model_get_path(GTK_TREE_MODEL(play_store), &iter);
401
start_playback_from_playlist(GtkTreePath * path) {
411
p = get_playing_path();
413
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, p);
414
gtk_tree_path_free(p);
418
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, path);
419
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) > 0) {
420
GtkTreeIter iter_child;
421
gtk_tree_model_iter_children(GTK_TREE_MODEL(play_store), &iter_child, &iter);
422
mark_track(&iter_child);
427
p = get_playing_path();
428
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, p);
429
gtk_tree_path_free(p);
430
cue_track_for_playback(&iter, &cue);
432
if (options.show_sn_title) {
436
toggle_noeffect(PLAY, TRUE);
440
toggle_noeffect(PAUSE, FALSE);
444
rb_write(rb_gui2disk, &cmd, sizeof(char));
445
rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
446
try_waking_disk_thread();
453
playlist_window_close(GtkWidget * widget, GdkEvent * event, gpointer data) {
455
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_toggle), FALSE);
462
playlist_window_key_pressed(GtkWidget * widget, GdkEventKey * kevent) {
465
GtkTreeViewColumn * column;
471
switch (kevent->keyval) {
475
if (kevent->state & GDK_SHIFT_MASK) { /* SHIFT + Insert */
476
add_directory(NULL, NULL);
478
add_files(NULL, NULL);
484
if (!options.playlist_is_embedded) {
485
playlist_window_close(NULL, NULL, NULL);
491
gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &path, &column);
494
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, path) &&
495
!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(play_store), &iter)) {
499
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
500
COLUMN_TRACK_NAME, &pname, COLUMN_PHYSICAL_FILENAME, &pfile, -1);
502
strncpy(fileinfo_name, pname, MAXLEN-1);
503
strncpy(fileinfo_file, pfile, MAXLEN-1);
506
show_file_info(fileinfo_name, fileinfo_file, 0, NULL, dummy);
511
gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &path, &column);
514
start_playback_from_playlist(path);
523
plist__search_cb(NULL);
527
if (kevent->state & GDK_CONTROL_MASK) {
528
if (kevent->state & GDK_SHIFT_MASK) {
534
show_active_position_in_playlist_toggle();
539
gtk_tree_view_collapse_all(GTK_TREE_VIEW(play_list));
540
show_active_position_in_playlist();
544
if (kevent->state & GDK_SHIFT_MASK) { /* SHIFT + Delete */
546
} else if (kevent->state & GDK_CONTROL_MASK) { /* CTRL + Delete */
555
aifp_transfer_files();
558
#endif /* HAVE_IFP */
561
expand_collapse_album_node();
570
doubleclick_handler(GtkWidget * widget, GdkEventButton * event, gpointer func_data) {
573
GtkTreeViewColumn * column;
578
if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
580
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(play_list), event->x, event->y,
581
&path, &column, NULL, NULL)) {
582
start_playback_from_playlist(path);
586
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
587
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(play_list), event->x, event->y,
588
&path, &column, NULL, NULL) &&
589
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, path) &&
590
!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(play_store), &iter)) {
592
if (gtk_tree_selection_count_selected_rows(play_select) == 0)
593
gtk_tree_view_set_cursor(GTK_TREE_VIEW(play_list), path, NULL, FALSE);
595
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
596
COLUMN_TRACK_NAME, &pname, COLUMN_PHYSICAL_FILENAME, &pfile, -1);
598
strncpy(fileinfo_name, pname, MAXLEN-1);
599
strncpy(fileinfo_file, pfile, MAXLEN-1);
603
gtk_widget_set_sensitive(plist__fileinfo, TRUE);
605
gtk_widget_set_sensitive(plist__fileinfo, FALSE);
608
gtk_widget_set_sensitive(plist__rva, (vol_window == NULL) ? TRUE : FALSE);
610
gtk_menu_popup(GTK_MENU(plist_menu), NULL, NULL, NULL, NULL,
611
event->button, event->time);
619
filter(const struct dirent * de) {
621
return de->d_name[0] != '.';
626
finalize_add_to_playlist(gpointer data) {
628
--pl_progress_bar_semaphore;
630
if (playlist_window != NULL && pl_progress_bar_semaphore == 0) {
631
playlist_content_changed();
632
delayed_playlist_rearrange(100);
633
playlist_progress_bar_hide();
634
select_active_position_in_playlist();
641
/* if data == NULL clears playlist */
643
add_file_to_playlist(gpointer data) {
645
playlist_filemeta * plfm = (playlist_filemeta *)data;
648
AQUALUNG_MUTEX_LOCK(playlist_wait_mutex)
655
gchar voladj_str[32];
656
gchar duration_str[MAXLEN];
659
voladj2str(plfm->voladj, voladj_str);
660
time2time_na(plfm->duration, duration_str);
662
if (plfm->level == 0) {
664
/* deactivate toplevel item if playing path already exists */
665
if (plfm->active && (path = get_playing_path()) != NULL) {
667
gtk_tree_path_free(path);
670
gtk_tree_store_append(play_store, &iter, NULL);
673
int n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), NULL);
676
/* someone viciously cleared the list while adding tracks to album node;
677
ignore further tracks to this node */
681
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &parent, NULL, n-1);
682
gtk_tree_store_append(play_store, &iter, &parent);
685
gtk_tree_store_set(play_store, &iter,
686
COLUMN_TRACK_NAME, plfm->title,
687
COLUMN_PHYSICAL_FILENAME, plfm->filename,
688
COLUMN_SELECTION_COLOR, plfm->active ? pl_color_active : pl_color_inactive,
689
COLUMN_VOLUME_ADJUSTMENT, plfm->voladj,
690
COLUMN_VOLUME_ADJUSTMENT_DISP, voladj_str,
691
COLUMN_DURATION, plfm->duration,
692
COLUMN_DURATION_DISP, duration_str,
693
COLUMN_FONT_WEIGHT, (plfm->active && options.show_active_track_name_in_bold) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
699
playlist_filemeta_free(plfm);
702
--playlist_data_written;
704
if (playlist_data_written == 0) {
705
AQUALUNG_COND_SIGNAL(playlist_thread_wait)
708
AQUALUNG_MUTEX_UNLOCK(playlist_wait_mutex)
715
playlist_thread_add_to_list(playlist_filemeta * plfm) {
717
AQUALUNG_MUTEX_LOCK(playlist_wait_mutex)
718
++playlist_data_written;
720
g_idle_add(add_file_to_playlist, (gpointer)plfm);
722
if (playlist_data_written > 100) {
723
AQUALUNG_COND_WAIT(playlist_thread_wait, playlist_wait_mutex)
726
AQUALUNG_MUTEX_UNLOCK(playlist_wait_mutex)
731
add_files_to_playlist_thread(void * arg) {
736
AQUALUNG_THREAD_DETACH()
738
AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
740
lfiles = (GSList *)arg;
742
for (node = lfiles; node; node = node->next) {
744
if (!playlist_thread_stop) {
745
playlist_filemeta * plfm = NULL;
747
if ((plfm = playlist_filemeta_get((char *)node->data, NULL, 1)) != NULL) {
749
playlist_thread_add_to_list(plfm);
756
g_slist_free(lfiles);
758
g_idle_add(finalize_add_to_playlist, NULL);
760
AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
767
add_files(GtkWidget * widget, gpointer data) {
771
dialog = gtk_file_chooser_dialog_new(_("Select files"),
772
options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
773
GTK_FILE_CHOOSER_ACTION_OPEN,
774
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
775
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
779
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
780
gtk_window_set_default_size(GTK_WINDOW(dialog), 580, 390);
781
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
782
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), options.currdir);
783
assign_audio_fc_filters(GTK_FILE_CHOOSER(dialog));
784
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
786
if (options.show_hidden) {
787
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
790
if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
794
strncpy(options.currdir, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)),
797
lfiles = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
799
playlist_progress_bar_show();
800
AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, add_files_to_playlist_thread, lfiles)
803
gtk_widget_destroy(dialog);
808
add_dir_to_playlist(char * dirname) {
811
struct dirent ** ent;
816
if (playlist_thread_stop) {
820
n = scandir(dirname, &ent, filter, alphasort);
821
for (i = 0; i < n; i++) {
823
if (playlist_thread_stop) {
827
snprintf(path, MAXLEN-1, "%s/%s", dirname, ent[i]->d_name);
829
if (stat(path, &st_file) == -1) {
834
if (S_ISDIR(st_file.st_mode)) {
835
add_dir_to_playlist(path);
837
playlist_filemeta * plfm = NULL;
839
if ((plfm = playlist_filemeta_get(path, NULL, 1)) != NULL) {
841
playlist_thread_add_to_list(plfm);
860
add_dir_to_playlist_thread(void * arg) {
866
AQUALUNG_THREAD_DETACH()
868
AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
870
lfiles = (GSList *)arg;
872
for (node = lfiles; node; node = node->next) {
874
add_dir_to_playlist((char *)node->data);
878
g_slist_free(lfiles);
880
g_idle_add(finalize_add_to_playlist, NULL);
882
AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
889
add_directory(GtkWidget * widget, gpointer data) {
893
dialog = gtk_file_chooser_dialog_new(_("Select directory"),
894
options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
895
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
896
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
897
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
901
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
902
gtk_window_set_default_size(GTK_WINDOW(dialog), 580, 390);
903
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
904
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), options.currdir);
905
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
907
if (options.show_hidden) {
908
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
911
if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
915
strncpy(options.currdir, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)),
918
lfiles = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
920
playlist_progress_bar_show();
921
AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, add_dir_to_playlist_thread, lfiles)
924
gtk_widget_destroy(dialog);
929
plist__save_cb(gpointer data) {
932
gchar * selected_filename;
935
dialog = gtk_file_chooser_dialog_new(_("Please specify the file to save the playlist to."),
936
options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
937
GTK_FILE_CHOOSER_ACTION_SAVE,
938
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
939
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
943
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), options.currdir);
944
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "playlist.xml");
946
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
947
gtk_window_set_default_size(GTK_WINDOW(dialog), 580, 390);
948
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
950
if (options.show_hidden) {
951
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
954
if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
956
selected_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
957
strncpy(options.currdir, selected_filename, MAXLEN-1);
959
save_playlist(selected_filename);
961
g_free(selected_filename);
964
gtk_widget_destroy(dialog);
969
playlist_load_thread(void * arg) {
971
char * filename = (char *)arg;
972
char fullname[MAXLEN];
973
playlist_filemeta * plfm = NULL;
976
AQUALUNG_THREAD_DETACH()
978
AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
984
normalize_filename(filename, fullname);
987
switch (is_playlist(fullname)) {
989
if ((plfm = playlist_filemeta_get(fullname, NULL, 1)) == NULL) {
990
fprintf(stderr, "playlist_load_thread(): playlist_filemeta_get() returned NULL\n");
992
playlist_thread_add_to_list(NULL);
993
playlist_thread_add_to_list(plfm);
997
load_playlist(fullname, 0);
1000
load_m3u(fullname, 0);
1003
load_pls(fullname, 0);
1007
g_idle_add(finalize_add_to_playlist, NULL);
1009
AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
1016
plist__load_cb(gpointer data) {
1019
const gchar * selected_filename;
1022
dialog = gtk_file_chooser_dialog_new(_("Please specify the file to load the playlist from."),
1023
options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
1024
GTK_FILE_CHOOSER_ACTION_OPEN,
1025
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1026
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1030
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1031
gtk_window_set_default_size(GTK_WINDOW(dialog), 580, 390);
1032
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), options.currdir);
1033
assign_playlist_fc_filters(GTK_FILE_CHOOSER(dialog));
1034
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1036
if (options.show_hidden) {
1037
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
1040
if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1042
selected_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1043
strncpy(options.currdir, selected_filename, MAXLEN-1);
1045
playlist_progress_bar_show();
1047
AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, playlist_load_thread, strdup(selected_filename))
1050
gtk_widget_destroy(dialog);
1055
playlist_enqueue_thread(void * arg) {
1057
char * filename = (char *)arg;
1058
char fullname[MAXLEN];
1059
playlist_filemeta * plfm = NULL;
1062
AQUALUNG_THREAD_DETACH()
1064
AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
1070
normalize_filename(filename, fullname);
1073
switch (is_playlist(fullname)) {
1075
if ((plfm = playlist_filemeta_get(fullname, NULL, 1)) == NULL) {
1076
fprintf(stderr, "playlist_enqueue_thread(): playlist_filemeta_get() returned NULL\n");
1078
playlist_thread_add_to_list(plfm);
1082
load_playlist(fullname, 1);
1085
load_m3u(fullname, 1);
1088
load_pls(fullname, 1);
1092
g_idle_add(finalize_add_to_playlist, NULL);
1094
AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
1101
plist__enqueue_cb(gpointer data) {
1104
const gchar * selected_filename;
1107
dialog = gtk_file_chooser_dialog_new(_("Please specify the file to load the playlist from."),
1108
options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
1109
GTK_FILE_CHOOSER_ACTION_OPEN,
1110
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1111
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1115
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), options.currdir);
1116
assign_playlist_fc_filters(GTK_FILE_CHOOSER(dialog));
1118
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1119
gtk_window_set_default_size(GTK_WINDOW(dialog), 580, 390);
1120
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1122
if (options.show_hidden) {
1123
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
1126
if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1128
selected_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1129
strncpy(options.currdir, selected_filename, MAXLEN-1);
1131
playlist_progress_bar_show();
1132
AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, playlist_enqueue_thread, strdup(selected_filename))
1135
gtk_widget_destroy(dialog);
1140
watch_vol_calc(gpointer data) {
1142
gfloat * volumes = (gfloat *)data;
1144
if (!vol_finished) {
1148
if (vol_index != vol_n_tracks) {
1154
if (vol_is_average) {
1155
gchar voladj_str[32];
1156
gfloat voladj = rva_from_multiple_volumes(vol_n_tracks, volumes,
1157
options.rva_use_linear_thresh,
1158
options.rva_avg_linear_thresh,
1159
options.rva_avg_stddev_thresh,
1161
options.rva_steepness);
1164
voladj2str(voladj, voladj_str);
1166
for (i = 0; i < vol_n_tracks; i++) {
1168
if (gtk_tree_store_iter_is_valid(play_store, &vol_iters[i])) {
1169
gtk_tree_store_set(play_store, &vol_iters[i],
1170
COLUMN_VOLUME_ADJUSTMENT, voladj, COLUMN_VOLUME_ADJUSTMENT_DISP, voladj_str, -1);
1175
gchar voladj_str[32];
1179
for (i = 0; i < vol_n_tracks; i++) {
1180
if (gtk_tree_store_iter_is_valid(play_store, &vol_iters[i])) {
1181
voladj = rva_from_volume(volumes[i],
1183
options.rva_steepness);
1184
voladj2str(voladj, voladj_str);
1185
gtk_tree_store_set(play_store, &vol_iters[i], COLUMN_VOLUME_ADJUSTMENT, voladj,
1186
COLUMN_VOLUME_ADJUSTMENT_DISP, voladj_str, -1);
1200
plist_setup_vol_foreach(GtkTreeIter * iter, void * data) {
1204
gtk_tree_model_get(GTK_TREE_MODEL(play_store), iter, 1, &pfile, -1);
1206
if (pl_vol_queue == NULL) {
1207
pl_vol_queue = vol_queue_push(NULL, pfile, *iter/*dummy*/);
1209
vol_queue_push(pl_vol_queue, pfile, *iter/*dummy*/);
1213
vol_iters = (GtkTreeIter *)realloc(vol_iters, vol_n_tracks * sizeof(GtkTreeIter));
1215
fprintf(stderr, "realloc error in plist_setup_vol_calc()\n");
1218
vol_iters[vol_n_tracks-1] = *iter;
1224
plist_setup_vol_calc(void) {
1226
gfloat * volumes = NULL;
1228
pl_vol_queue = NULL;
1230
if (vol_window != NULL) {
1236
playlist_foreach_selected(plist_setup_vol_foreach, NULL);
1238
if (vol_n_tracks == 0)
1241
if (!options.rva_is_enabled) {
1243
GtkWidget * dialog = gtk_dialog_new_with_buttons(
1245
options.playlist_is_embedded ?
1246
GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
1247
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1248
GTK_STOCK_YES, GTK_RESPONSE_ACCEPT,
1249
GTK_STOCK_NO, GTK_RESPONSE_REJECT,
1252
GtkWidget * label = gtk_label_new(_("Playback RVA is currently disabled.\n"
1253
"Do you want to enable it now?"));
1255
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, TRUE, 10);
1256
gtk_widget_show(label);
1258
if (aqualung_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1261
gtk_widget_destroy(dialog);
1265
options.rva_is_enabled = 1;
1266
gtk_widget_destroy(dialog);
1270
if ((volumes = calloc(vol_n_tracks, sizeof(float))) == NULL) {
1271
fprintf(stderr, "calloc error in plist__rva_separate_cb()\n");
1278
calculate_volume(pl_vol_queue, volumes);
1279
g_timeout_add(200, watch_vol_calc, (gpointer)volumes);
1284
plist__rva_separate_cb(gpointer data) {
1287
plist_setup_vol_calc();
1292
plist__rva_average_cb(gpointer data) {
1295
plist_setup_vol_calc();
1300
plist__reread_file_meta_foreach(GtkTreeIter * iter, void * data) {
1304
gchar voladj_str[32];
1305
gchar duration_str[MAXLEN];
1307
playlist_filemeta * plfm = NULL;
1311
gtk_tree_model_get(GTK_TREE_MODEL(play_store), iter,
1312
COLUMN_TRACK_NAME, &title,
1313
COLUMN_PHYSICAL_FILENAME, &fullname,
1316
if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(play_store), &dummy, iter)) {
1322
plfm = playlist_filemeta_get(fullname, title, composit);
1324
fprintf(stderr, "plist__reread_file_meta_foreach(): "
1325
"playlist_filemeta_get() returned NULL\n");
1331
voladj2str(plfm->voladj, voladj_str);
1332
time2time_na(plfm->duration, duration_str);
1334
gtk_tree_store_set(play_store, iter,
1335
COLUMN_TRACK_NAME, plfm->title,
1336
COLUMN_PHYSICAL_FILENAME, fullname,
1337
COLUMN_VOLUME_ADJUSTMENT, plfm->voladj, COLUMN_VOLUME_ADJUSTMENT_DISP, voladj_str,
1338
COLUMN_DURATION, plfm->duration, COLUMN_DURATION_DISP, duration_str,
1341
playlist_filemeta_free(plfm);
1349
plist__reread_file_meta_cb(gpointer data) {
1352
GtkTreeIter iter_child;
1357
playlist_foreach_selected(plist__reread_file_meta_foreach, NULL);
1360
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
1362
if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(play_store), &iter)) {
1366
if (gtk_tree_selection_iter_is_selected(play_select, &iter)) {
1370
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store),
1371
&iter_child, &iter, j++)) {
1372
if (gtk_tree_selection_iter_is_selected(play_select,
1381
recalc_album_node(&iter);
1386
delayed_playlist_rearrange(100);
1392
plist__send_songs_to_iriver_cb(gpointer data) {
1394
aifp_transfer_files();
1397
#endif /* HAVE_IFP */
1400
plist__fileinfo_cb(gpointer data) {
1404
show_file_info(fileinfo_name, fileinfo_file, 0, NULL, dummy);
1408
plist__search_cb(gpointer data) {
1410
search_playlist_dialog();
1415
add_cb(GtkWidget * widget, GdkEvent * event) {
1417
if (event->type == GDK_BUTTON_PRESS) {
1418
GdkEventButton * bevent = (GdkEventButton *) event;
1420
if (bevent->button == 3) {
1422
gtk_menu_popup(GTK_MENU(add_menu), NULL, NULL, NULL, NULL,
1423
bevent->button, bevent->time);
1434
sel_cb(GtkWidget * widget, GdkEvent * event) {
1436
if (event->type == GDK_BUTTON_PRESS) {
1437
GdkEventButton * bevent = (GdkEventButton *) event;
1439
if (bevent->button == 3) {
1441
gtk_menu_popup(GTK_MENU(sel_menu), NULL, NULL, NULL, NULL,
1442
bevent->button, bevent->time);
1453
rem_cb(GtkWidget * widget, GdkEvent * event) {
1455
if (event->type == GDK_BUTTON_PRESS) {
1456
GdkEventButton * bevent = (GdkEventButton *) event;
1458
if (bevent->button == 3) {
1460
gtk_menu_popup(GTK_MENU(rem_menu), NULL, NULL, NULL, NULL,
1461
bevent->button, bevent->time);
1471
/* if alt_name != NULL, it will be used as title if no meta is found */
1473
playlist_filemeta_get(char * physical_name, char * alt_name, int composit) {
1475
gchar display_name[MAXLEN];
1476
gchar artist_name[MAXLEN];
1477
gchar record_name[MAXLEN];
1478
gchar track_name[MAXLEN];
1479
metadata * meta = NULL;
1483
#if defined(HAVE_MOD) && (defined(HAVE_LIBZ) || defined(HAV_LIBBZ2))
1485
#endif /* (HAVE_MOD && (HAVE_LIBZ || HAVE_LIBBZ2)) */
1488
if (is_valid_mod_extension(physical_name)) {
1493
if ((pos = strrchr(physical_name, '.')) != NULL) {
1496
if (!strcasecmp(pos, "gz")) {
1501
if (!strcasecmp(pos, "bz2")) {
1504
#endif /* HAVE LIBBZ2 */
1507
#endif /* HAVE LIBZ */
1509
#endif /* HAVE_MOD */
1511
playlist_filemeta * plfm = calloc(1, sizeof(playlist_filemeta));
1513
fprintf(stderr, "calloc error in playlist_filemeta_get()\n");
1517
if ((plfm->duration = get_file_duration(physical_name)) < 0.0f) {
1521
if (options.rva_is_enabled) {
1523
if (meta != NULL && meta_read(meta, physical_name)) {
1524
if (!meta_get_rva(meta, &(plfm->voladj))) {
1525
plfm->voladj = 0.0f;
1528
plfm->voladj = 0.0f;
1535
plfm->voladj = 0.0f;
1538
artist_name[0] = '\0';
1539
record_name[0] = '\0';
1540
track_name[0] = '\0';
1542
if (options.auto_use_ext_meta_artist ||
1543
options.auto_use_ext_meta_record ||
1544
options.auto_use_ext_meta_track) {
1547
if (meta != NULL && !meta_read(meta, physical_name)) {
1554
if ((meta != NULL) && options.auto_use_ext_meta_artist) {
1555
meta_get_artist(meta, artist_name);
1556
if (artist_name[0] != '\0') {
1561
if ((meta != NULL) && options.auto_use_ext_meta_record) {
1562
meta_get_record(meta, record_name);
1563
if (record_name[0] != '\0') {
1568
if ((meta != NULL) && options.auto_use_ext_meta_track) {
1569
meta_get_title(meta, track_name);
1570
if (track_name[0] != '\0') {
1575
if ((artist_name[0] != '\0') ||
1576
(record_name[0] != '\0') ||
1577
(track_name[0] != '\0')) {
1579
if (artist_name[0] == '\0') {
1580
strcpy(artist_name, _("Unknown"));
1582
if (record_name[0] == '\0') {
1583
strcpy(record_name, _("Unknown"));
1585
if (track_name[0] == '\0') {
1586
strcpy(track_name, _("Unknown"));
1592
if (use_meta && (meta != NULL)) {
1594
make_title_string(display_name, options.title_format,
1595
artist_name, record_name, track_name);
1597
strncpy(display_name, track_name, MAXLEN-1);
1601
if (alt_name != NULL) {
1602
strcpy(display_name, alt_name);
1604
if ((substr = strrchr(physical_name, '/')) == NULL) {
1605
substr = physical_name;
1609
substr = g_filename_display_name(substr);
1610
strcpy(display_name, substr);
1619
plfm->filename = g_strdup(physical_name);
1620
plfm->title = g_strdup(display_name);
1627
playlist_filemeta_free(playlist_filemeta * plfm) {
1629
free(plfm->filename);
1636
add__files_cb(gpointer data) {
1638
add_files(NULL, NULL);
1643
add__dir_cb(gpointer data) {
1645
add_directory(NULL, NULL);
1650
select_all(GtkWidget * widget, gpointer data) {
1652
gtk_tree_selection_select_all(play_select);
1657
sel__all_cb(gpointer data) {
1659
select_all(NULL, NULL);
1664
sel__none_cb(gpointer data) {
1666
gtk_tree_selection_unselect_all(play_select);
1671
sel__inv_foreach(GtkTreePath * path, gpointer data) {
1673
gtk_tree_selection_unselect_path(play_select, path);
1674
gtk_tree_path_free(path);
1679
sel__inv_cb(gpointer data) {
1681
GList * list = gtk_tree_selection_get_selected_rows(play_select, NULL);
1683
g_signal_handlers_block_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
1685
gtk_tree_selection_select_all(play_select);
1686
g_list_foreach(list, (GFunc)sel__inv_foreach, NULL);
1689
g_signal_handlers_unblock_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
1691
playlist_selection_changed(NULL, NULL);
1696
rem__all_cb(gpointer data) {
1698
g_signal_handlers_block_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
1700
gtk_tree_store_clear(play_store);
1702
g_signal_handlers_unblock_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
1704
playlist_selection_changed(NULL, NULL);
1705
playlist_content_changed();
1709
rem__sel_foreach(GtkTreePath * path, gpointer data) {
1714
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, path);
1715
gtk_tree_store_remove(play_store, &iter);
1716
gtk_tree_path_free(path);
1721
rem__sel_compare(gconstpointer p1, gconstpointer p2) {
1723
return gtk_tree_path_compare((GtkTreePath *)p1, (GtkTreePath *)p2);
1727
rem__sel_save_expanded(GtkTreeView * tree_view, GtkTreePath * path, GList ** list) {
1731
if ((iter = (GtkTreeIter *)malloc(sizeof(GtkTreeIter))) == NULL) {
1732
fprintf(stderr, "playlist.c: rem__sel_save_expanded(): malloc error\n");
1736
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), iter, path);
1738
*list = g_list_append(*list, iter);
1742
rem__sel_restore_expanded(GtkTreeIter * iter, gpointer data) {
1744
if (gtk_tree_store_iter_is_valid(play_store, iter)) {
1746
GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(play_store), iter);
1748
gtk_tree_view_expand_row(GTK_TREE_VIEW(play_list), path, FALSE);
1749
gtk_tree_path_free(path);
1757
rem__sel_cb(gpointer data) {
1759
GList * remove_list = gtk_tree_selection_get_selected_rows(play_select, NULL);
1760
GList * expand_list = NULL;
1761
GList * recalc_list = NULL;
1762
GList * node = NULL;
1765
GtkTreePath * last_path;
1766
GtkTreeIter last_iter;
1768
if (remove_list == NULL) {
1772
last_path = gtk_tree_path_copy((GtkTreePath *)g_list_first(remove_list)->data);
1774
gtk_tree_view_map_expanded_rows(GTK_TREE_VIEW(play_list),
1775
(GtkTreeViewMappingFunc)rem__sel_save_expanded, &expand_list);
1777
g_object_ref(play_store);
1779
g_signal_handlers_block_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
1780
gtk_tree_view_set_model(GTK_TREE_VIEW(play_list), NULL);
1781
g_signal_handlers_unblock_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
1783
remove_list = g_list_sort(remove_list, rem__sel_compare);
1788
if (gtk_tree_path_get_depth((GtkTreePath *)node->data) == 1) {
1790
/* if an album node is selected, it would be redundant to keep its children selected */
1792
for (n = node->next; n && gtk_tree_path_is_ancestor((GtkTreePath *)node->data,
1793
(GtkTreePath *)n->data); n = n->next) {
1794
gtk_tree_path_free((GtkTreePath *)n->data);
1800
/* if all children of an album node are selected, replace the children with the
1801
parent, so the whole album node will be removed */
1803
GtkTreeIter iter_parent;
1804
GtkTreePath * path_parent = gtk_tree_path_copy((GtkTreePath *)node->data);
1808
gtk_tree_path_up(path_parent);
1809
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter_parent, path_parent);
1811
i = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter_parent);
1814
for (n = node; n && gtk_tree_path_is_ancestor(path_parent, (GtkTreePath *)n->data); n = n->next) {
1819
gtk_tree_path_free(node->data);
1820
node->data = path_parent;
1823
for (n = node->next; i < c; n = n->next, i++) {
1824
gtk_tree_path_free((GtkTreePath *)n->data);
1828
recalc_list = g_list_append(recalc_list, gtk_tree_iter_copy(&iter_parent));
1829
gtk_tree_path_free(path_parent);
1836
remove_list = g_list_reverse(remove_list);
1837
g_list_foreach(remove_list, (GFunc)rem__sel_foreach, NULL);
1838
g_list_free(remove_list);
1840
g_list_foreach(recalc_list, (GFunc)recalc_album_node, NULL);
1841
g_list_free(recalc_list);
1843
gtk_tree_view_set_model(GTK_TREE_VIEW(play_list), GTK_TREE_MODEL(play_store));
1845
g_list_foreach(expand_list, (GFunc)rem__sel_restore_expanded, NULL);
1846
g_list_free(expand_list);
1849
if (gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &last_iter, last_path)) {
1850
set_cursor_in_playlist(&last_iter, FALSE);
1851
} else if (gtk_tree_path_prev(last_path) &&
1852
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &last_iter, last_path)) {
1853
set_cursor_in_playlist(&last_iter, FALSE);
1856
gtk_tree_path_free(last_path);
1858
playlist_content_changed();
1859
playlist_selection_changed(NULL, NULL);
1864
rem__dead_cb(gpointer data) {
1870
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
1872
GtkTreeIter iter_child;
1876
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_PHYSICAL_FILENAME, &filename, -1);
1878
gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
1880
if (n == 0 && strstr(filename, "CDDA") != filename &&
1881
g_file_test (filename, G_FILE_TEST_IS_REGULAR) == FALSE) {
1883
gtk_tree_store_remove(play_store, &iter);
1892
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, &iter, j++)) {
1893
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter_child, 1, &filename, -1);
1895
if (strstr(filename, "CDDA") != filename &&
1896
g_file_test (filename, G_FILE_TEST_IS_REGULAR) == FALSE) {
1897
gtk_tree_store_remove(play_store, &iter_child);
1904
/* remove album node if empty */
1905
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) == 0) {
1906
gtk_tree_store_remove(play_store, &iter);
1909
recalc_album_node(&iter);
1914
playlist_content_changed();
1918
remove_sel(GtkWidget * widget, gpointer data) {
1924
/* playlist item is selected -> keep, else -> remove
1925
* ret: 0 if kept, 1 if removed track
1928
cut_track_item(GtkTreeIter * piter) {
1930
if (!gtk_tree_selection_iter_is_selected(play_select, piter)) {
1931
gtk_tree_store_remove(play_store, piter);
1938
/* ret: 1 if at least one of album node's children are selected; else 0 */
1940
any_tracks_selected(GtkTreeIter * piter) {
1943
GtkTreeIter iter_child;
1945
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, piter, j++)) {
1946
if (gtk_tree_selection_iter_is_selected(play_select, &iter_child)) {
1954
/* cut selected callback */
1956
cut__sel_cb(gpointer data) {
1961
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
1963
gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
1965
if (n) { /* album node */
1966
if (any_tracks_selected(&iter)) {
1968
GtkTreeIter iter_child;
1969
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store),
1970
&iter_child, &iter, j++)) {
1971
j -= cut_track_item(&iter_child);
1974
recalc_album_node(&iter);
1976
i -= cut_track_item(&iter);
1978
} else { /* track node */
1979
i -= cut_track_item(&iter);
1982
gtk_tree_selection_unselect_all(play_select);
1983
playlist_content_changed();
1988
playlist_rearrange_timeout_cb(gpointer data) {
1990
playlist_size_allocate(NULL, NULL);
1997
delayed_playlist_rearrange(int delay) {
1999
g_timeout_add(delay, playlist_rearrange_timeout_cb, NULL);
2004
playlist_size_allocate(GtkWidget * widget, GdkEventConfigure * event) {
2012
if (main_window == NULL || playlist_window == NULL) {
2016
avail = play_list->allocation.width;
2018
if (gtk_tree_view_column_get_visible(GTK_TREE_VIEW_COLUMN(rva_column))) {
2019
gtk_tree_view_column_cell_get_size(GTK_TREE_VIEW_COLUMN(rva_column),
2020
NULL, NULL, NULL, &rva_width, NULL);
2025
if (gtk_tree_view_column_get_visible(GTK_TREE_VIEW_COLUMN(length_column))) {
2026
gtk_tree_view_column_cell_get_size(GTK_TREE_VIEW_COLUMN(length_column),
2027
NULL, NULL, NULL, &length_width, NULL);
2032
track_width = avail - rva_width - length_width;
2033
if (track_width < 1)
2036
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(track_column), track_width);
2037
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(rva_column), rva_width);
2038
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(length_column), length_width);
2041
if (options.playlist_is_embedded) {
2042
if (main_window->window != NULL) {
2043
gtk_widget_queue_draw(main_window);
2046
if (playlist_window->window != NULL) {
2047
gtk_widget_queue_draw(playlist_window);
2055
playlist_child_stats(GtkTreeIter * iter, int * count, float * duration, float * songs_size, int selected) {
2059
struct stat statbuf;
2060
GtkTreeIter iter_child;
2062
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, iter, j++)) {
2064
if (!selected || gtk_tree_selection_iter_is_selected(play_select, &iter_child)) {
2068
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter_child, COLUMN_DURATION, &len,
2069
COLUMN_PHYSICAL_FILENAME, &tstr, -1);
2072
if (stat(tstr, &statbuf) != -1) {
2073
*songs_size += statbuf.st_size / 1024.0;
2082
playlist_stats_set_busy() {
2084
gtk_label_set_text(GTK_LABEL(statusbar_total), _("counting..."));
2088
/* if selected == true -> stats for selected tracks; else: all tracks */
2090
playlist_stats(int selected) {
2096
gfloat duration = 0;
2101
gfloat songs_size, m_size;
2102
struct stat statbuf;
2104
if (!options.enable_playlist_statusbar) {
2108
if (main_window == NULL || playlist_window == NULL) {
2114
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
2116
gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
2117
if (n > 0) { /* album node -- count children tracks */
2118
if (gtk_tree_selection_iter_is_selected(play_select, &iter)) {
2119
playlist_child_stats(&iter, &count, &duration, &songs_size, 0/*false*/);
2121
playlist_child_stats(&iter, &count, &duration, &songs_size, selected);
2124
if (!selected || gtk_tree_selection_iter_is_selected(play_select, &iter)) {
2125
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_DURATION, &len,
2126
COLUMN_PHYSICAL_FILENAME, &tstr, -1);
2129
if (stat(tstr, &statbuf) != -1) {
2130
songs_size += statbuf.st_size / 1024.0;
2138
time2time(duration, time);
2139
m_size = songs_size / 1024.0;
2142
if (options.pl_statusbar_show_size && (m_size > 0)) {
2143
if (m_size < 1024) {
2144
sprintf(str, _("%d track [%s] (%.1f MB)"), count, time, m_size);
2146
sprintf(str, _("%d track [%s] (%.1f GB)"), count, time, m_size / 1024.0);
2149
sprintf(str, _("%d track [%s] "), count, time);
2152
if (options.pl_statusbar_show_size && (m_size > 0)) {
2153
if (m_size < 1024) {
2154
sprintf(str, _("%d tracks [%s] (%.1f MB)"), count, time, m_size);
2156
sprintf(str, _("%d tracks [%s] (%.1f GB)"), count, time, m_size / 1024.0);
2159
sprintf(str, _("%d tracks [%s] "), count, time);
2164
gtk_label_set_text(GTK_LABEL(statusbar_selected), str);
2166
gtk_label_set_text(GTK_LABEL(statusbar_total), str);
2172
recalc_album_node(GtkTreeIter * iter) {
2174
gfloat duration = 0;
2179
playlist_child_stats(iter, &count, &duration, &songs_size, 0/*false*/);
2180
time2time(duration, time);
2181
gtk_tree_store_set(play_store, iter, COLUMN_DURATION, duration, COLUMN_DURATION_DISP, time, -1);
2183
gtk_tree_model_get(GTK_TREE_MODEL(play_store), iter, COLUMN_SELECTION_COLOR, &color, -1);
2184
if (strcmp(color, pl_color_active) == 0) {
2193
playlist_selection_changed(GtkTreeSelection * sel, gpointer data) {
2195
playlist_stats(1/* true */);
2200
playlist_content_changed(void) {
2202
if (pl_progress_bar_semaphore == 0) {
2203
playlist_stats(0/*false*/);
2209
playlist_drag_data_get(GtkWidget * widget, GdkDragContext * drag_context,
2210
GtkSelectionData * data, guint info, guint time, gpointer user_data) {
2212
gtk_selection_data_set(data, data->target, 8, (const guchar *) "list\0", 5);
2217
playlist_perform_drag(GtkTreeModel * model,
2218
GtkTreeIter * sel_iter, GtkTreePath * sel_path,
2219
GtkTreeIter * pos_iter, GtkTreePath * pos_path) {
2221
gint cmp = gtk_tree_path_compare(sel_path, pos_path);
2222
gint sel_depth = gtk_tree_path_get_depth(sel_path);
2223
gint pos_depth = gtk_tree_path_get_depth(pos_path);
2225
gint * sel_idx = gtk_tree_path_get_indices(sel_path);
2226
gint * pos_idx = gtk_tree_path_get_indices(pos_path);
2232
if (sel_depth == pos_depth && (sel_depth == 1 /* top */ || sel_idx[0] == pos_idx[0])) {
2237
gtk_tree_store_move_before(play_store, sel_iter, pos_iter);
2239
gtk_tree_store_move_after(play_store, sel_iter, pos_iter);
2242
if (gtk_tree_model_iter_parent(model, &parent, sel_iter)) {
2243
unmark_track(&parent);
2244
mark_track(&parent);
2249
GtkTreeIter sel_parent;
2250
GtkTreeIter pos_parent;
2251
gint recalc_sel_parent = 0;
2252
gint recalc_pos_parent = 0;
2262
if (gtk_tree_model_iter_has_child(model, sel_iter)) {
2266
if (gtk_tree_model_iter_parent(model, &sel_parent, sel_iter)) {
2267
recalc_sel_parent = 1;
2270
if (gtk_tree_model_iter_parent(model, &pos_parent, pos_iter)) {
2271
recalc_pos_parent = 1;
2274
gtk_tree_model_get(model, sel_iter, COLUMN_TRACK_NAME, &tname,
2275
COLUMN_PHYSICAL_FILENAME, &fname, COLUMN_SELECTION_COLOR, &color,
2276
COLUMN_VOLUME_ADJUSTMENT, &voladj, COLUMN_VOLUME_ADJUSTMENT_DISP, &voldisp,
2277
COLUMN_DURATION, &duration, COLUMN_DURATION_DISP, &durdisp,
2278
COLUMN_FONT_WEIGHT, &fontw, -1);
2281
gtk_tree_store_insert_before(play_store, &iter, NULL, pos_iter);
2283
gtk_tree_store_insert_after(play_store, &iter, NULL, pos_iter);
2286
gtk_tree_store_set(play_store, &iter, COLUMN_TRACK_NAME, tname, COLUMN_PHYSICAL_FILENAME, fname,
2287
COLUMN_SELECTION_COLOR, color, COLUMN_VOLUME_ADJUSTMENT, voladj,
2288
COLUMN_VOLUME_ADJUSTMENT_DISP, voldisp, COLUMN_DURATION, duration,
2289
COLUMN_DURATION_DISP, durdisp, COLUMN_FONT_WEIGHT, fontw, -1);
2291
gtk_tree_store_remove(play_store, sel_iter);
2293
if (recalc_sel_parent) {
2294
if (gtk_tree_model_iter_has_child(model, &sel_parent)) {
2295
recalc_album_node(&sel_parent);
2296
unmark_track(&sel_parent);
2297
mark_track(&sel_parent);
2299
gtk_tree_store_remove(play_store, &sel_parent);
2303
if (recalc_pos_parent) {
2304
recalc_album_node(&pos_parent);
2305
unmark_track(&pos_parent);
2306
mark_track(&pos_parent);
2319
playlist_remove_scroll_tags() {
2321
if (playlist_scroll_up_tag > 0) {
2322
g_source_remove(playlist_scroll_up_tag);
2323
playlist_scroll_up_tag = -1;
2325
if (playlist_scroll_dn_tag > 0) {
2326
g_source_remove(playlist_scroll_dn_tag);
2327
playlist_scroll_dn_tag = -1;
2332
playlist_drag_data_received(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y,
2333
GtkSelectionData * data, guint info, guint time) {
2335
GtkTreeViewColumn * column;
2337
if (info == 0) { /* drag and drop inside Aqualung */
2339
if (!strcmp((gchar *)data->data, "store")) {
2341
GtkTreePath * path = NULL;
2342
GtkTreeIter * piter = NULL;
2345
GtkTreeModel * model;
2348
if (gtk_tree_selection_get_selected(music_select, &model, &iter)) {
2349
depth = gtk_tree_path_get_depth(gtk_tree_model_get_path(model, &iter));
2351
if (is_store_iter_cdda(&iter))
2353
#endif /* HAVE_CDDA */
2358
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(play_list),
2359
x, y, &path, &column, NULL, NULL)) {
2361
if (depth != 4) { /* dragging store, artist or record */
2362
while (gtk_tree_path_get_depth(path) > 1) {
2363
gtk_tree_path_up(path);
2367
gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, path);
2373
store__addlist_defmode(piter);
2376
artist__addlist_defmode(piter);
2379
record__addlist_defmode(piter);
2382
track__addlist_cb(piter);
2384
if (piter && gtk_tree_model_iter_parent(GTK_TREE_MODEL(play_store),
2386
recalc_album_node(&parent);
2387
unmark_track(&parent);
2388
mark_track(&parent);
2395
gtk_tree_path_free(path);
2398
} else if (!strcmp((gchar *)data->data, "list")) {
2400
GtkTreeModel * model;
2401
GtkTreeIter sel_iter;
2402
GtkTreeIter pos_iter;
2403
GtkTreePath * sel_path = NULL;
2404
GtkTreePath * pos_path = NULL;
2406
gtk_tree_selection_set_mode(play_select, GTK_SELECTION_SINGLE);
2408
if (gtk_tree_selection_get_selected(play_select, &model, &sel_iter)) {
2410
sel_path = gtk_tree_model_get_path(model, &sel_iter);
2412
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(play_list),
2413
x, y, &pos_path, &column, NULL, NULL)) {
2415
gtk_tree_model_get_iter(model, &pos_iter, pos_path);
2417
playlist_perform_drag(model,
2418
&sel_iter, sel_path,
2419
&pos_iter, pos_path);
2424
gtk_tree_path_free(sel_path);
2428
gtk_tree_path_free(pos_path);
2431
gtk_tree_selection_set_mode(play_select, GTK_SELECTION_MULTIPLE);
2434
} else { /* drag and drop from external app */
2440
struct stat st_file;
2442
for (i = 0; *((gchar *)data->data + i); i++) {
2443
if (*((gchar *)data->data + i) == '\r') {
2444
*((gchar *)data->data + i) = '\n';
2448
uri_list = g_strsplit((gchar *)data->data, "\n", 0);
2450
for (i = 0; uri_list[i]; i++) {
2452
if (*(uri_list[i]) == '\0') {
2456
if ((str = g_filename_from_uri(uri_list[i], NULL, NULL)) != NULL) {
2457
strncpy(file, str, MAXLEN-1);
2462
if (strstr(uri_list[i], "file:") == uri_list[i] ||
2463
strstr(uri_list[i], "FILE:") == uri_list[i]) {
2467
while (uri_list[i][off] == '/' && uri_list[i][off+1] == '/') {
2471
strncpy(file, uri_list[i] + off, MAXLEN-1);
2474
if ((str = g_locale_from_utf8(file, -1, NULL, NULL, NULL)) != NULL) {
2475
strncpy(file, str, MAXLEN-1);
2479
if (stat(file, &st_file) == 0) {
2481
GSList * list = g_slist_append(NULL, strdup(file));
2483
playlist_progress_bar_show();
2484
if (S_ISDIR(st_file.st_mode)) {
2485
AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL,
2486
add_dir_to_playlist_thread, list)
2488
AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL,
2489
add_files_to_playlist_thread, list)
2494
g_strfreev(uri_list);
2496
playlist_remove_scroll_tags();
2503
playlist_scroll_up(gpointer data) {
2505
g_signal_emit_by_name(G_OBJECT(scrolled_win), "scroll-child",
2506
GTK_SCROLL_STEP_BACKWARD, FALSE/*vertical*/, NULL);
2512
playlist_scroll_dn(gpointer data) {
2514
g_signal_emit_by_name(G_OBJECT(scrolled_win), "scroll-child",
2515
GTK_SCROLL_STEP_FORWARD, FALSE/*vertical*/, NULL);
2522
playlist_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time) {
2524
if (playlist_drag_y < widget->allocation.height / 2) {
2525
if (playlist_scroll_up_tag == -1) {
2526
playlist_scroll_up_tag = g_timeout_add(100, playlist_scroll_up, NULL);
2529
if (playlist_scroll_dn_tag == -1) {
2530
playlist_scroll_dn_tag = g_timeout_add(100, playlist_scroll_dn, NULL);
2537
playlist_drag_motion(GtkWidget * widget, GdkDragContext * context,
2538
gint x, gint y, guint time) {
2540
playlist_drag_y = y;
2541
playlist_remove_scroll_tags();
2548
playlist_drag_end(GtkWidget * widget, GdkDragContext * drag_context, gpointer data) {
2550
playlist_remove_scroll_tags();
2557
playlist_add_cdda(GtkTreeIter * iter_drive, unsigned long hash) {
2562
int target_found = 0;
2563
GtkTreeIter target_iter;
2566
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
2568
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) > 0) {
2574
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &child, &iter, j++)) {
2577
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &child,
2578
COLUMN_PHYSICAL_FILENAME, &pfile, -1);
2580
if (!target_found && strstr(pfile, "CDDA") == pfile) {
2584
if (cdda_hash_matches(pfile, hash)) {
2592
if (!target_found && !has_cdda) {
2599
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
2600
COLUMN_PHYSICAL_FILENAME, &pfile, -1);
2602
if (!target_found && strstr(pfile, "CDDA") != pfile) {
2607
if (cdda_hash_matches(pfile, hash)) {
2617
record_addlist_iter(*iter_drive, &target_iter, options.playlist_is_tree);
2619
record_addlist_iter(*iter_drive, NULL, options.playlist_is_tree);
2624
playlist_remove_cdda(char * device_path) {
2630
cdda_drive_t * drive = cdda_get_drive_by_device_path(device_path);
2633
if (drive == NULL) {
2637
if (drive->disc.hash == 0) {
2638
hash = drive->disc.hash_prev;
2640
hash = drive->disc.hash;
2643
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
2645
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) > 0) {
2650
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &child, &iter, j++)) {
2653
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &child,
2654
COLUMN_PHYSICAL_FILENAME, &pfile, -1);
2656
if (cdda_hash_matches(pfile, hash)) {
2657
gtk_tree_store_remove(play_store, &child);
2664
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) == 0) {
2665
gtk_tree_store_remove(play_store, &iter);
2668
recalc_album_node(&iter);
2674
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
2675
COLUMN_PHYSICAL_FILENAME, &pfile, -1);
2677
if (cdda_hash_matches(pfile, hash)) {
2678
gtk_tree_store_remove(play_store, &iter);
2686
playlist_content_changed();
2689
#endif /* HAVE_CDDA */
2693
create_playlist(void) {
2697
GtkWidget * hbox_bottom;
2698
GtkWidget * add_button;
2699
GtkWidget * selall_button;
2700
GtkWidget * remsel_button;
2702
GtkWidget * viewport;
2704
GtkWidget * statusbar;
2705
GtkWidget * statusbar_scrolledwin;
2706
GtkWidget * statusbar_viewport;
2713
GtkTargetEntry source_table[] = {
2714
{ "STRING", GTK_TARGET_SAME_APP, 0 }
2717
GtkTargetEntry target_table[] = {
2718
{ "STRING", GTK_TARGET_SAME_APP, 0 },
2720
{ "text/plain", 0, 1 },
2721
{ "text/uri-list", 0, 1 }
2724
/* window creating stuff */
2725
if (!options.playlist_is_embedded) {
2726
playlist_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2727
gtk_window_set_title(GTK_WINDOW(playlist_window), _("Playlist"));
2728
gtk_container_set_border_width(GTK_CONTAINER(playlist_window), 2);
2729
g_signal_connect(G_OBJECT(playlist_window), "delete_event",
2730
G_CALLBACK(playlist_window_close), NULL);
2731
gtk_widget_set_size_request(playlist_window, 300, 200);
2733
gtk_widget_set_size_request(playlist_window, 200, 200);
2736
if (!options.playlist_is_embedded) {
2737
g_signal_connect(G_OBJECT(playlist_window), "key_press_event",
2738
G_CALLBACK(playlist_window_key_pressed), NULL);
2741
gtk_widget_set_events(playlist_window, GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
2744
if (!options.playlist_is_embedded) {
2745
plist_menu = gtk_menu_new();
2746
init_plist_menu(plist_menu);
2749
vbox = gtk_vbox_new(FALSE, 2);
2750
gtk_container_add(GTK_CONTAINER(playlist_window), vbox);
2752
/* create playlist */
2754
play_store = gtk_tree_store_new(NUMBER_OF_COLUMNS,
2755
G_TYPE_STRING, /* track name */
2756
G_TYPE_STRING, /* physical filename */
2757
G_TYPE_STRING, /* color for selections */
2758
G_TYPE_FLOAT, /* volume adjustment [dB] */
2759
G_TYPE_STRING, /* volume adj. displayed */
2760
G_TYPE_FLOAT, /* duration (in secs) */
2761
G_TYPE_STRING, /* duration displayed */
2762
G_TYPE_INT); /* font weight */
2766
play_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(play_store));
2767
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(play_list), FALSE);
2768
gtk_widget_set_name(play_list, "play_list");
2769
gtk_widget_set_size_request(play_list, 100, 100);
2771
if (options.override_skin_settings) {
2772
gtk_widget_modify_font (play_list, fd_playlist);
2775
if (options.enable_pl_rules_hint) {
2776
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(play_list), TRUE);
2779
playlist_color_is_set = 0;
2781
if (pl_color_active[0] == '\0') {
2782
strcpy(pl_color_active, "#ffffff");
2784
if (pl_color_inactive[0] == '\0') {
2785
strcpy(pl_color_inactive, "#010101");
2788
for (i = 0; i < 3; i++) {
2789
switch (options.plcol_idx[i]) {
2791
track_renderer = gtk_cell_renderer_text_new();
2792
track_column = gtk_tree_view_column_new_with_attributes("Tracks",
2798
gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(track_column),
2799
GTK_TREE_VIEW_COLUMN_FIXED);
2800
gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(track_column), 3);
2801
gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(track_column), FALSE);
2802
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(track_column), 10);
2803
g_object_set(G_OBJECT(track_renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2804
gtk_cell_renderer_set_fixed_size(track_renderer, 10, -1);
2805
gtk_tree_view_append_column(GTK_TREE_VIEW(play_list), track_column);
2809
rva_renderer = gtk_cell_renderer_text_new();
2810
g_object_set((gpointer)rva_renderer, "xalign", 1.0, NULL);
2811
rva_column = gtk_tree_view_column_new_with_attributes("RVA",
2817
gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(rva_column),
2818
GTK_TREE_VIEW_COLUMN_FIXED);
2819
gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(rva_column), 3);
2820
if (options.show_rva_in_playlist) {
2821
gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(rva_column), TRUE);
2822
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(rva_column), 50);
2824
gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(rva_column), FALSE);
2825
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(rva_column), 1);
2827
gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(rva_column), FALSE);
2828
gtk_tree_view_append_column(GTK_TREE_VIEW(play_list), rva_column);
2832
length_renderer = gtk_cell_renderer_text_new();
2833
g_object_set((gpointer)length_renderer, "xalign", 1.0, NULL);
2834
length_column = gtk_tree_view_column_new_with_attributes("Length",
2840
gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(length_column),
2841
GTK_TREE_VIEW_COLUMN_FIXED);
2842
gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(length_column), 3);
2843
if (options.show_length_in_playlist) {
2844
gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(length_column), TRUE);
2845
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(length_column), 50);
2847
gtk_tree_view_column_set_visible(GTK_TREE_VIEW_COLUMN(length_column), FALSE);
2848
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(length_column), 1);
2850
gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(length_column), FALSE);
2851
gtk_tree_view_append_column(GTK_TREE_VIEW(play_list), length_column);
2855
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(play_list), FALSE);
2858
/* setup drag and drop */
2860
gtk_drag_source_set(play_list,
2866
gtk_drag_dest_set(play_list,
2867
GTK_DEST_DEFAULT_ALL,
2870
GDK_ACTION_MOVE | GDK_ACTION_COPY);
2872
snprintf(path, MAXLEN-1, "%s/drag.png", AQUALUNG_DATADIR);
2873
if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
2874
gtk_drag_source_set_icon_pixbuf(play_list, pixbuf);
2877
g_signal_connect(G_OBJECT(play_list), "drag_end",
2878
G_CALLBACK(playlist_drag_end), NULL);
2880
g_signal_connect(G_OBJECT(play_list), "drag_data_get",
2881
G_CALLBACK(playlist_drag_data_get), NULL);
2883
g_signal_connect(G_OBJECT(play_list), "drag_leave",
2884
G_CALLBACK(playlist_drag_leave), NULL);
2886
g_signal_connect(G_OBJECT(play_list), "drag_motion",
2887
G_CALLBACK(playlist_drag_motion), NULL);
2889
g_signal_connect(G_OBJECT(play_list), "drag_data_received",
2890
G_CALLBACK(playlist_drag_data_received), NULL);
2893
play_select = gtk_tree_view_get_selection(GTK_TREE_VIEW(play_list));
2894
gtk_tree_selection_set_mode(play_select, GTK_SELECTION_MULTIPLE);
2896
g_signal_connect(G_OBJECT(play_select), "changed",
2897
G_CALLBACK(playlist_selection_changed), NULL);
2900
g_signal_connect(G_OBJECT(play_list), "button_press_event",
2901
G_CALLBACK(doubleclick_handler), NULL);
2904
viewport = gtk_viewport_new(NULL, NULL);
2905
gtk_box_pack_start(GTK_BOX(vbox), viewport, TRUE, TRUE, 0);
2907
scrolled_win = gtk_scrolled_window_new(NULL, NULL);
2908
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
2909
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2910
gtk_container_add(GTK_CONTAINER(viewport), scrolled_win);
2912
gtk_container_add(GTK_CONTAINER(scrolled_win), play_list);
2915
pl_progress_bar_container = gtk_hbox_new(FALSE, 4);
2916
gtk_box_pack_start(GTK_BOX(vbox), pl_progress_bar_container, FALSE, FALSE, 0);
2918
if (pl_progress_bar_semaphore > 0) {
2919
playlist_progress_bar_show();
2923
if (options.enable_playlist_statusbar) {
2925
statusbar_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
2926
GTK_WIDGET_UNSET_FLAGS(statusbar_scrolledwin, GTK_CAN_FOCUS);
2927
gtk_widget_set_size_request(statusbar_scrolledwin, 1, -1); /* MAGIC */
2928
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(statusbar_scrolledwin),
2929
GTK_POLICY_NEVER, GTK_POLICY_NEVER);
2931
statusbar_viewport = gtk_viewport_new(NULL, NULL);
2932
gtk_widget_set_name(statusbar_viewport, "info_viewport");
2934
gtk_container_add(GTK_CONTAINER(statusbar_scrolledwin), statusbar_viewport);
2935
gtk_box_pack_start(GTK_BOX(vbox), statusbar_scrolledwin, FALSE, TRUE, 2);
2937
gtk_widget_set_events(statusbar_viewport,
2938
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
2940
g_signal_connect(G_OBJECT(statusbar_viewport), "button_press_event",
2941
G_CALLBACK(scroll_btn_pressed), NULL);
2942
g_signal_connect(G_OBJECT(statusbar_viewport), "button_release_event",
2943
G_CALLBACK(scroll_btn_released), (gpointer)statusbar_scrolledwin);
2944
g_signal_connect(G_OBJECT(statusbar_viewport), "motion_notify_event",
2945
G_CALLBACK(scroll_motion_notify), (gpointer)statusbar_scrolledwin);
2947
statusbar = gtk_hbox_new(FALSE, 0);
2948
gtk_container_set_border_width(GTK_CONTAINER(statusbar), 1);
2949
gtk_container_add(GTK_CONTAINER(statusbar_viewport), statusbar);
2951
statusbar_selected = gtk_label_new("");
2952
gtk_widget_set_name(statusbar_selected, "label_info");
2953
gtk_box_pack_end(GTK_BOX(statusbar), statusbar_selected, FALSE, TRUE, 0);
2955
statusbar_selected_label = gtk_label_new(_("Selected: "));
2956
gtk_widget_set_name(statusbar_selected_label, "label_info");
2957
gtk_box_pack_end(GTK_BOX(statusbar), statusbar_selected_label, FALSE, TRUE, 0);
2959
gtk_box_pack_end(GTK_BOX(statusbar), gtk_vseparator_new(), FALSE, TRUE, 5);
2961
statusbar_total = gtk_label_new("");
2962
gtk_widget_set_name(statusbar_total, "label_info");
2963
gtk_box_pack_end(GTK_BOX(statusbar), statusbar_total, FALSE, TRUE, 0);
2965
statusbar_total_label = gtk_label_new(_("Total: "));
2966
gtk_widget_set_name(statusbar_total_label, "label_info");
2967
gtk_box_pack_end(GTK_BOX(statusbar), statusbar_total_label, FALSE, TRUE, 0);
2969
if (options.override_skin_settings) {
2970
gtk_widget_modify_font (statusbar_selected, fd_statusbar);
2971
gtk_widget_modify_font (statusbar_selected_label, fd_statusbar);
2972
gtk_widget_modify_font (statusbar_total, fd_statusbar);
2973
gtk_widget_modify_font (statusbar_total_label, fd_statusbar);
2976
playlist_selection_changed(NULL, NULL);
2977
playlist_content_changed();
2981
/* bottom area of playlist window */
2982
hbox_bottom = gtk_hbox_new(FALSE, 0);
2983
gtk_box_pack_start(GTK_BOX(vbox), hbox_bottom, FALSE, TRUE, 0);
2985
add_button = gtk_button_new_with_label(_("Add files"));
2986
GTK_WIDGET_UNSET_FLAGS(add_button, GTK_CAN_FOCUS);
2987
gtk_tooltips_set_tip (GTK_TOOLTIPS (aqualung_tooltips), add_button, _("Add files to playlist\n(Press right mouse button for menu)"), NULL);
2988
gtk_box_pack_start(GTK_BOX(hbox_bottom), add_button, TRUE, TRUE, 0);
2989
g_signal_connect(G_OBJECT(add_button), "clicked", G_CALLBACK(add_files), NULL);
2991
selall_button = gtk_button_new_with_label(_("Select all"));
2992
GTK_WIDGET_UNSET_FLAGS(selall_button, GTK_CAN_FOCUS);
2993
gtk_tooltips_set_tip (GTK_TOOLTIPS (aqualung_tooltips), selall_button, _("Select all songs in playlist\n(Press right mouse button for menu)"), NULL);
2994
gtk_box_pack_start(GTK_BOX(hbox_bottom), selall_button, TRUE, TRUE, 0);
2995
g_signal_connect(G_OBJECT(selall_button), "clicked", G_CALLBACK(select_all), NULL);
2997
remsel_button = gtk_button_new_with_label(_("Remove selected"));
2998
GTK_WIDGET_UNSET_FLAGS(remsel_button, GTK_CAN_FOCUS);
2999
gtk_tooltips_set_tip (GTK_TOOLTIPS (aqualung_tooltips), remsel_button, _("Remove selected songs from playlist\n(Press right mouse button for menu)"), NULL);
3000
gtk_box_pack_start(GTK_BOX(hbox_bottom), remsel_button, TRUE, TRUE, 0);
3001
g_signal_connect(G_OBJECT(remsel_button), "clicked", G_CALLBACK(remove_sel), NULL);
3004
add_menu = gtk_menu_new();
3006
add__dir = gtk_menu_item_new_with_label(_("Add directory"));
3007
gtk_menu_shell_append(GTK_MENU_SHELL(add_menu), add__dir);
3008
g_signal_connect_swapped(G_OBJECT(add__dir), "activate", G_CALLBACK(add__dir_cb), NULL);
3009
gtk_widget_show(add__dir);
3011
add__files = gtk_menu_item_new_with_label(_("Add files"));
3012
gtk_menu_shell_append(GTK_MENU_SHELL(add_menu), add__files);
3013
g_signal_connect_swapped(G_OBJECT(add__files), "activate", G_CALLBACK(add__files_cb), NULL);
3014
gtk_widget_show(add__files);
3016
g_signal_connect_swapped(G_OBJECT(add_button), "event", G_CALLBACK(add_cb), NULL);
3019
sel_menu = gtk_menu_new();
3021
sel__none = gtk_menu_item_new_with_label(_("Select none"));
3022
gtk_menu_shell_append(GTK_MENU_SHELL(sel_menu), sel__none);
3023
g_signal_connect_swapped(G_OBJECT(sel__none), "activate", G_CALLBACK(sel__none_cb), NULL);
3024
gtk_widget_show(sel__none);
3026
sel__inv = gtk_menu_item_new_with_label(_("Invert selection"));
3027
gtk_menu_shell_append(GTK_MENU_SHELL(sel_menu), sel__inv);
3028
g_signal_connect_swapped(G_OBJECT(sel__inv), "activate", G_CALLBACK(sel__inv_cb), NULL);
3029
gtk_widget_show(sel__inv);
3031
sel__all = gtk_menu_item_new_with_label(_("Select all"));
3032
gtk_menu_shell_append(GTK_MENU_SHELL(sel_menu), sel__all);
3033
g_signal_connect_swapped(G_OBJECT(sel__all), "activate", G_CALLBACK(sel__all_cb), NULL);
3034
gtk_widget_show(sel__all);
3036
g_signal_connect_swapped(G_OBJECT(selall_button), "event", G_CALLBACK(sel_cb), NULL);
3039
rem_menu = gtk_menu_new();
3041
rem__all = gtk_menu_item_new_with_label(_("Remove all"));
3042
gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), rem__all);
3043
g_signal_connect_swapped(G_OBJECT(rem__all), "activate", G_CALLBACK(rem__all_cb), NULL);
3044
gtk_widget_show(rem__all);
3046
rem__dead = gtk_menu_item_new_with_label(_("Remove dead"));
3047
gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), rem__dead);
3048
g_signal_connect_swapped(G_OBJECT(rem__dead), "activate", G_CALLBACK(rem__dead_cb), NULL);
3049
gtk_widget_show(rem__dead);
3051
cut__sel = gtk_menu_item_new_with_label(_("Cut selected"));
3052
gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), cut__sel);
3053
g_signal_connect_swapped(G_OBJECT(cut__sel), "activate", G_CALLBACK(cut__sel_cb), NULL);
3054
gtk_widget_show(cut__sel);
3056
rem__sel = gtk_menu_item_new_with_label(_("Remove selected"));
3057
gtk_menu_shell_append(GTK_MENU_SHELL(rem_menu), rem__sel);
3058
g_signal_connect_swapped(G_OBJECT(rem__sel), "activate", G_CALLBACK(rem__sel_cb), NULL);
3059
gtk_widget_show(rem__sel);
3061
g_signal_connect_swapped(G_OBJECT(remsel_button), "event", G_CALLBACK(rem_cb), NULL);
3064
g_signal_connect(G_OBJECT(playlist_window), "size_allocate",
3065
G_CALLBACK(playlist_size_allocate), NULL);
3072
show_playlist(void) {
3074
options.playlist_on = 1;
3076
if (!options.playlist_is_embedded) {
3077
gtk_window_move(GTK_WINDOW(playlist_window), options.playlist_pos_x, options.playlist_pos_y);
3078
gtk_window_resize(GTK_WINDOW(playlist_window), options.playlist_size_x, options.playlist_size_y);
3080
gtk_window_get_size(GTK_WINDOW(main_window), &options.main_size_x, &options.main_size_y);
3081
gtk_window_resize(GTK_WINDOW(main_window), options.main_size_x, options.main_size_y + options.playlist_size_y + 6);
3084
gtk_widget_show_all(playlist_window);
3086
if (!playlist_color_is_set) {
3087
set_playlist_color();
3088
playlist_color_is_set = 1;
3094
hide_playlist(void) {
3096
options.playlist_on = 0;
3097
if (!options.playlist_is_embedded) {
3098
gtk_window_get_position(GTK_WINDOW(playlist_window), &options.playlist_pos_x, &options.playlist_pos_y);
3099
gtk_window_get_size(GTK_WINDOW(playlist_window), &options.playlist_size_x, &options.playlist_size_y);
3101
options.playlist_size_x = playlist_window->allocation.width;
3102
options.playlist_size_y = playlist_window->allocation.height;
3103
gtk_window_get_size(GTK_WINDOW(main_window), &options.main_size_x, &options.main_size_y);
3105
gtk_widget_hide(playlist_window);
3107
if (options.playlist_is_embedded) {
3108
gtk_window_resize(GTK_WINDOW(main_window), options.main_size_x, options.main_size_y - options.playlist_size_y - 6);
3115
save_track_node(GtkTreeIter * piter, xmlNodePtr root, char * nodeID) {
3119
gchar *converted_temp;
3126
gtk_tree_model_get(GTK_TREE_MODEL(play_store), piter,
3127
COLUMN_TRACK_NAME, &track_name, COLUMN_PHYSICAL_FILENAME, &phys_name,
3128
COLUMN_SELECTION_COLOR, &color,
3129
COLUMN_VOLUME_ADJUSTMENT, &voladj, COLUMN_DURATION, &duration, -1);
3131
node = xmlNewTextChild(root, NULL, (const xmlChar*) nodeID, NULL);
3133
xmlNewTextChild(node, NULL, (const xmlChar*) "track_name", (const xmlChar*) track_name);
3134
if (!strcmp(nodeID,"record")) {
3135
xmlNewTextChild(node, NULL, (const xmlChar*) "phys_name", (const xmlChar*) phys_name);
3137
if ((strlen(phys_name) > 4) &&
3138
(phys_name[0] == 'C') &&
3139
(phys_name[1] == 'D') &&
3140
(phys_name[2] == 'D') &&
3141
(phys_name[3] == 'A')) {
3142
converted_temp = strdup(phys_name);
3144
converted_temp = g_filename_to_uri(phys_name, NULL, NULL);
3146
xmlNewTextChild(node, NULL, (const xmlChar*) "phys_name", (const xmlChar*) converted_temp);
3147
g_free(converted_temp);
3150
/* FIXME: don't use #000000 (black) color for active song */
3152
if (strcmp(color, pl_color_active) == 0) {
3157
xmlNewTextChild(node, NULL, (const xmlChar*) "is_active", (const xmlChar*) str);
3159
snprintf(str, 31, "%f", voladj);
3160
xmlNewTextChild(node, NULL, (const xmlChar*) "voladj", (const xmlChar*) str);
3162
snprintf(str, 31, "%f", duration);
3163
xmlNewTextChild(node, NULL, (const xmlChar*) "duration", (const xmlChar*) str);
3174
save_playlist(char * filename) {
3181
doc = xmlNewDoc((const xmlChar*) "1.0");
3182
root = xmlNewNode(NULL, (const xmlChar*) "aqualung_playlist");
3183
xmlDocSetRootElement(doc, root);
3185
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
3187
gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
3189
if (n) { /* album node */
3191
GtkTreeIter iter_child;
3194
node = save_track_node(&iter, root, "record");
3195
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store),
3196
&iter_child, &iter, j++)) {
3197
save_track_node(&iter_child, node, "track");
3199
} else { /* track node */
3200
save_track_node(&iter, root, "track");
3203
xmlSaveFormatFile(filename, doc, 1);
3209
parse_playlist_track(xmlDocPtr doc, xmlNodePtr _cur, int level) {
3212
gchar *converted_temp;
3213
gint is_record_node;
3216
playlist_filemeta * plfm = NULL;
3219
is_record_node = !xmlStrcmp(_cur->name, (const xmlChar *)"record");
3221
if ((plfm = (playlist_filemeta *)calloc(1, sizeof(playlist_filemeta))) == NULL) {
3222
fprintf(stderr, "calloc error in parse_playlist_track()\n");
3226
plfm->level = level;
3228
for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3230
if (!xmlStrcmp(cur->name, (const xmlChar *)"track_name")) {
3231
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
3233
plfm->title = strndup((char *)key, MAXLEN-1);
3236
} else if (!xmlStrcmp(cur->name, (const xmlChar *)"phys_name")) {
3237
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
3239
if (is_record_node) {
3240
/* "record" node keeps special data here */
3241
plfm->filename = strndup((char *)key, MAXLEN-1);
3242
} else if ((converted_temp = g_filename_from_uri((char *) key, NULL, NULL))) {
3243
plfm->filename = strndup(converted_temp, MAXLEN-1);
3244
g_free(converted_temp);
3246
/* try to read utf8 filename from outdated file */
3247
converted_temp = g_locale_from_utf8((char *) key, -1, NULL, NULL, NULL);
3248
if (converted_temp != NULL) {
3249
plfm->filename = strndup(converted_temp, MAXLEN-1);
3250
g_free(converted_temp);
3252
/* last try - maybe it's plain locale filename */
3253
plfm->filename = strndup((char *)key, MAXLEN-1);
3258
} else if (!xmlStrcmp(cur->name, (const xmlChar *)"is_active")) {
3259
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
3261
if (!xmlStrcmp(key, (const xmlChar *)"yes")) {
3270
} else if (!xmlStrcmp(cur->name, (const xmlChar *)"voladj")) {
3271
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
3273
plfm->voladj = convf((char *)key);
3276
} else if (!xmlStrcmp(cur->name, (const xmlChar *)"duration")) {
3277
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
3279
plfm->duration = convf((char *)key);
3285
playlist_thread_add_to_list(plfm);
3287
if (is_record_node) {
3289
for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3290
if (!xmlStrcmp(cur->name, (const xmlChar *)"track")) {
3291
parse_playlist_track(doc, cur, 1);
3298
load_playlist(char * filename, int enqueue) {
3304
if ((f = fopen(filename, "rt")) == NULL) {
3309
doc = xmlParseFile(filename);
3311
fprintf(stderr, "An XML error occured while parsing %s\n", filename);
3315
cur = xmlDocGetRootElement(doc);
3317
fprintf(stderr, "load_playlist: empty XML document\n");
3322
if (xmlStrcmp(cur->name, (const xmlChar *)"aqualung_playlist")) {
3323
fprintf(stderr, "load_playlist: XML document of the wrong type, "
3324
"root node != aqualung_playlist\n");
3330
playlist_thread_add_to_list(NULL);
3333
for (cur = cur->xmlChildrenNode; cur != NULL && !playlist_thread_stop; cur = cur->next) {
3334
if (!xmlStrcmp(cur->name, (const xmlChar *)"track") ||
3335
!xmlStrcmp(cur->name, (const xmlChar *)"record")) {
3336
parse_playlist_track(doc, cur, 0);
3345
load_m3u(char * filename, int enqueue) {
3352
gchar pl_dir[MAXLEN];
3358
playlist_filemeta * plfm = NULL;
3361
if ((str = strrchr(filename, '/')) == NULL) {
3362
printf("load_m3u(): programmer error: playlist path is not absolute\n");
3364
for (i = 0; (i < (str - filename)) && (i < MAXLEN-1); i++) {
3365
pl_dir[i] = filename[i];
3369
if ((f = fopen(filename, "rb")) == NULL) {
3370
fprintf(stderr, "unable to open .m3u playlist: %s\n", filename);
3375
playlist_thread_add_to_list(NULL);
3379
while ((c = fgetc(f)) != EOF && !playlist_thread_stop) {
3380
if ((c != '\n') && (c != '\r') && (i < MAXLEN)) {
3381
if ((i > 0) || ((c != ' ') && (c != '\t'))) {
3391
if (strstr(line, "#EXTM3U") == line) {
3395
if (strstr(line, "#EXTINF:") == line) {
3397
char str_duration[64];
3400
/* We parse the timing, but throw it away.
3401
This may change in the future. */
3402
while ((line[cnt+8] >= '0') && (line[cnt+8] <= '9') && cnt < 63) {
3403
str_duration[cnt] = line[cnt+8];
3406
str_duration[cnt] = '\0';
3407
snprintf(name, MAXLEN-1, "%s", line+cnt+9);
3411
/* safeguard against http:// and C:\ stuff */
3412
if (strstr(line, "http://") == line) {
3413
fprintf(stderr, "Ignoring playlist item: %s\n", line);
3418
if ((line[1] == ':') && (line[2] == '\\')) {
3419
fprintf(stderr, "Ignoring playlist item: %s\n", line);
3425
snprintf(path, MAXLEN-1, "%s", line);
3427
/* path curing: turn \-s into /-s */
3428
for (n = 0; n < strlen(path); n++) {
3429
if (path[n] == '\\')
3433
if (path[0] != '/') {
3434
strncpy(tmp, path, MAXLEN-1);
3435
snprintf(path, MAXLEN-1, "%s/%s", pl_dir, tmp);
3440
if ((ch = strrchr(path, '/')) != NULL) {
3442
snprintf(name, MAXLEN-1, "%s", ch);
3445
"warning: ain't this a directory? : %s\n", path);
3446
snprintf(name, MAXLEN-1, "%s", path);
3452
plfm = playlist_filemeta_get(path,
3453
have_name ? name : NULL,
3456
fprintf(stderr, "load_m3u(): playlist_filemeta_get() returned NULL\n");
3458
playlist_thread_add_to_list(plfm);
3467
load_pls(char * filename, int enqueue) {
3474
gchar pl_dir[MAXLEN];
3477
gchar title[MAXLEN];
3480
gint have_title = 0;
3481
gchar numstr_file[10];
3482
gchar numstr_title[10];
3483
playlist_filemeta * plfm = NULL;
3486
if ((str = strrchr(filename, '/')) == NULL) {
3487
printf("load_pls(): programmer error: playlist path is not absolute\n");
3489
for (i = 0; (i < (str - filename)) && (i < MAXLEN-1); i++) {
3490
pl_dir[i] = filename[i];
3494
if ((f = fopen(filename, "rb")) == NULL) {
3495
fprintf(stderr, "unable to open .pls playlist: %s\n", filename);
3500
playlist_thread_add_to_list(NULL);
3504
while ((c = fgetc(f)) != EOF && !playlist_thread_stop) {
3505
if ((c != '\n') && (c != '\r') && (i < MAXLEN)) {
3506
if ((i > 0) || ((c != ' ') && (c != '\t'))) {
3515
if (strstr(line, "[playlist]") == line) {
3518
if (strstr(line, "NumberOfEntries") == line) {
3521
if (strstr(line, "Version") == line) {
3525
if (strstr(line, "File") == line) {
3531
if ((ch = strstr(line, "=")) == NULL) {
3532
fprintf(stderr, "Syntax error in %s\n", filename);
3537
while ((*ch == ' ') || (*ch == '\t'))
3541
for (n = 0; (line[n+4] != '=') && (m < sizeof(numstr)); n++) {
3542
if ((line[n+4] != ' ') && (line[n+4] != '\t'))
3543
numstr[m++] = line[n+4];
3546
strncpy(numstr_file, numstr, sizeof(numstr_file));
3548
/* safeguard against http:// and C:\ stuff */
3549
if (strstr(ch, "http://") == ch) {
3550
fprintf(stderr, "Ignoring playlist item: %s\n", ch);
3552
have_file = have_title = 0;
3555
if ((ch[1] == ':') && (ch[2] == '\\')) {
3556
fprintf(stderr, "Ignoring playlist item: %s\n", ch);
3558
have_file = have_title = 0;
3562
snprintf(file, MAXLEN-1, "%s", ch);
3564
/* path curing: turn \-s into /-s */
3566
for (n = 0; n < strlen(file); n++) {
3567
if (file[n] == '\\')
3571
if (file[0] != '/') {
3572
strncpy(tmp, file, MAXLEN-1);
3573
snprintf(file, MAXLEN-1, "%s/%s", pl_dir, tmp);
3579
} else if (strstr(line, "Title") == line) {
3585
if ((ch = strstr(line, "=")) == NULL) {
3586
fprintf(stderr, "Syntax error in %s\n", filename);
3591
while ((*ch == ' ') || (*ch == '\t'))
3595
for (n = 0; (line[n+5] != '=') && (m < sizeof(numstr)); n++) {
3596
if ((line[n+5] != ' ') && (line[n+5] != '\t'))
3597
numstr[m++] = line[n+5];
3600
strncpy(numstr_title, numstr, sizeof(numstr_title));
3602
snprintf(title, MAXLEN-1, "%s", ch);
3606
} else if (strstr(line, "Length") == line) {
3608
/* We parse the timing, but throw it away.
3609
This may change in the future. */
3612
if ((ch = strstr(line, "=")) == NULL) {
3613
fprintf(stderr, "Syntax error in %s\n", filename);
3618
while ((*ch == ' ') || (*ch == '\t'))
3623
"Syntax error: invalid line in playlist: %s\n", line);
3627
if (!have_file || !have_title) {
3631
if (strcmp(numstr_file, numstr_title) != 0) {
3635
have_file = have_title = 0;
3637
plfm = playlist_filemeta_get(file,
3638
have_title ? title : NULL,
3641
fprintf(stderr, "load_pls(): playlist_filemeta_get() returned NULL\n");
3643
playlist_thread_add_to_list(plfm);
3652
* 1: native aqualung (XML)
3657
is_playlist(char * filename) {
3660
gchar buf[] = "<?xml version=\"1.0\"?>\n<aqualung_playlist";
3664
if ((f = fopen(filename, "rb")) == NULL) {
3667
if (fread(inbuf, 1, strlen(buf)+1, f) != strlen(buf)+1) {
3672
inbuf[strlen(buf)] = '\0';
3674
if (strcmp(buf, inbuf) == 0) {
3679
len = strlen(filename);
3684
if ((filename[len-4] == '.') &&
3685
((filename[len-3] == 'm') || (filename[len-3] == 'M')) &&
3686
(filename[len-2] == '3') &&
3687
((filename[len-1] == 'u') || (filename[len-1] == 'U'))) {
3691
if ((filename[len-4] == '.') &&
3692
((filename[len-3] == 'p') || (filename[len-3] == 'P')) &&
3693
((filename[len-2] == 'l') || (filename[len-2] == 'L')) &&
3694
((filename[len-1] == 's') || (filename[len-1] == 'S'))) {
3703
init_plist_menu(GtkWidget *append_menu) {
3705
plist__save = gtk_menu_item_new_with_label(_("Save playlist"));
3706
plist__load = gtk_menu_item_new_with_label(_("Load playlist"));
3707
plist__enqueue = gtk_menu_item_new_with_label(_("Enqueue playlist"));
3708
plist__separator1 = gtk_separator_menu_item_new();
3711
plist__send_songs_to_iriver = gtk_menu_item_new_with_label(_("Send to iFP device"));
3712
plist__separator3 = gtk_separator_menu_item_new();
3713
#endif /* HAVE_IFP */
3715
plist__rva = gtk_menu_item_new_with_label(_("Calculate RVA"));
3716
plist__rva_menu = gtk_menu_new();
3717
plist__rva_separate = gtk_menu_item_new_with_label(_("Separate"));
3718
plist__rva_average = gtk_menu_item_new_with_label(_("Average"));
3719
plist__reread_file_meta = gtk_menu_item_new_with_label(_("Reread file metadata"));
3720
plist__separator2 = gtk_separator_menu_item_new();
3721
plist__fileinfo = gtk_menu_item_new_with_label(_("File info..."));
3722
plist__search = gtk_menu_item_new_with_label(_("Search..."));
3724
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__save);
3725
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__load);
3726
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__enqueue);
3727
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__separator1);
3730
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__send_songs_to_iriver);
3731
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__separator3);
3732
#endif /* HAVE_IFP */
3734
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__rva);
3735
gtk_menu_item_set_submenu(GTK_MENU_ITEM(plist__rva), plist__rva_menu);
3736
gtk_menu_shell_append(GTK_MENU_SHELL(plist__rva_menu), plist__rva_separate);
3737
gtk_menu_shell_append(GTK_MENU_SHELL(plist__rva_menu), plist__rva_average);
3738
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__reread_file_meta);
3739
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__separator2);
3740
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__fileinfo);
3741
gtk_menu_shell_append(GTK_MENU_SHELL(append_menu), plist__search);
3743
g_signal_connect_swapped(G_OBJECT(plist__save), "activate", G_CALLBACK(plist__save_cb), NULL);
3744
g_signal_connect_swapped(G_OBJECT(plist__load), "activate", G_CALLBACK(plist__load_cb), NULL);
3745
g_signal_connect_swapped(G_OBJECT(plist__enqueue), "activate", G_CALLBACK(plist__enqueue_cb), NULL);
3746
g_signal_connect_swapped(G_OBJECT(plist__rva_separate), "activate", G_CALLBACK(plist__rva_separate_cb), NULL);
3747
g_signal_connect_swapped(G_OBJECT(plist__rva_average), "activate", G_CALLBACK(plist__rva_average_cb), NULL);
3748
g_signal_connect_swapped(G_OBJECT(plist__reread_file_meta), "activate", G_CALLBACK(plist__reread_file_meta_cb), NULL);
3751
g_signal_connect_swapped(G_OBJECT(plist__send_songs_to_iriver), "activate", G_CALLBACK(plist__send_songs_to_iriver_cb), NULL);
3752
#endif /* HAVE_IFP */
3754
g_signal_connect_swapped(G_OBJECT(plist__fileinfo), "activate", G_CALLBACK(plist__fileinfo_cb), NULL);
3755
g_signal_connect_swapped(G_OBJECT(plist__search), "activate", G_CALLBACK(plist__search_cb), NULL);
3757
gtk_widget_show(plist__save);
3758
gtk_widget_show(plist__load);
3759
gtk_widget_show(plist__enqueue);
3760
gtk_widget_show(plist__separator1);
3761
gtk_widget_show(plist__rva);
3762
gtk_widget_show(plist__rva_separate);
3763
gtk_widget_show(plist__rva_average);
3764
gtk_widget_show(plist__reread_file_meta);
3765
gtk_widget_show(plist__separator2);
3768
gtk_widget_show(plist__send_songs_to_iriver);
3769
gtk_widget_show(plist__separator3);
3770
#endif /* HAVE_IFP */
3772
gtk_widget_show(plist__fileinfo);
3773
gtk_widget_show(plist__search);
3778
show_active_position_in_playlist(void) {
3783
GtkTreePath * visible_path;
3784
GtkTreeViewColumn * visible_column;
3788
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(play_store), &iter)) {
3791
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_SELECTION_COLOR, &str, -1);
3792
if (strcmp(str, pl_color_active) == 0) {
3794
if (gtk_tree_selection_iter_is_selected(play_select, &iter)) {
3795
gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &visible_path, &visible_column);
3801
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(play_store), &iter));
3804
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, 0);
3807
set_cursor_in_playlist(&iter, TRUE);
3812
show_active_position_in_playlist_toggle(void) {
3814
GtkTreeIter iter, iter_child;
3816
gint flag, cflag, j;
3817
GtkTreePath * visible_path;
3818
GtkTreeViewColumn * visible_column;
3822
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(play_store), &iter)) {
3825
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_SELECTION_COLOR, &str, -1);
3826
if (strcmp(str, pl_color_active) == 0) {
3828
if (gtk_tree_selection_iter_is_selected(play_select, &iter) &&
3829
gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter)) {
3831
gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &visible_path, &visible_column);
3833
if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(play_list), visible_path)) {
3834
gtk_tree_view_expand_row(GTK_TREE_VIEW(play_list), visible_path, FALSE);
3839
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, &iter, j++)) {
3840
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter_child, COLUMN_SELECTION_COLOR, &str, -1);
3841
if (strcmp(str, pl_color_active) == 0) {
3850
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(play_store), &iter));
3854
set_cursor_in_playlist(&iter_child, TRUE);
3856
set_cursor_in_playlist(&iter, TRUE);
3863
show_last_position_in_playlist(void) {
3867
GtkTreePath * visible_path;
3869
for (i = 0; gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i); i++);
3872
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i-1);
3874
visible_path = gtk_tree_model_get_path (GTK_TREE_MODEL(play_store), &iter);
3876
gtk_tree_view_set_cursor (GTK_TREE_VIEW (play_list), visible_path, NULL, TRUE);
3877
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (play_list), visible_path,
3878
NULL, TRUE, 1.0, 0.0);
3879
gtk_tree_path_free(visible_path);
3885
expand_collapse_album_node(void) {
3887
GtkTreeIter iter, iter_child;
3890
GtkTreeViewColumn *column;
3894
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
3896
if (gtk_tree_selection_iter_is_selected(play_select, &iter) &&
3897
gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter)) {
3899
gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &path, &column);
3901
if (path && gtk_tree_view_row_expanded(GTK_TREE_VIEW(play_list), path)) {
3902
gtk_tree_view_collapse_row(GTK_TREE_VIEW(play_list), path);
3904
gtk_tree_view_expand_row(GTK_TREE_VIEW(play_list), path, FALSE);
3912
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, &iter, j++)) {
3914
if (gtk_tree_selection_iter_is_selected(play_select, &iter_child) &&
3915
gtk_tree_model_iter_parent(GTK_TREE_MODEL(play_store), &iter, &iter_child)) {
3917
path = gtk_tree_model_get_path (GTK_TREE_MODEL(play_store), &iter);
3920
gtk_tree_view_collapse_row(GTK_TREE_VIEW(play_list), path);
3921
gtk_tree_view_set_cursor (GTK_TREE_VIEW (play_list), path, NULL, TRUE);
3930
pl_progress_bar_update(gpointer data) {
3932
if (pl_progress_bar != NULL) {
3933
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pl_progress_bar));
3941
pl_progress_bar_stop_clicked(GtkWidget * widget, gpointer data) {
3943
playlist_thread_stop = 1;
3947
playlist_progress_bar_show(void) {
3949
++pl_progress_bar_semaphore;
3951
if (pl_progress_bar != NULL) {
3955
playlist_thread_stop = 0;
3957
playlist_stats_set_busy();
3959
pl_progress_bar = gtk_progress_bar_new();
3960
gtk_box_pack_start(GTK_BOX(pl_progress_bar_container), pl_progress_bar, TRUE, TRUE, 0);
3962
pl_progress_bar_stop_button = gtk_button_new_with_label(_("Stop adding files"));
3963
gtk_box_pack_start(GTK_BOX(pl_progress_bar_container), pl_progress_bar_stop_button, FALSE, FALSE, 0);
3964
g_signal_connect(G_OBJECT(pl_progress_bar_stop_button), "clicked",
3965
G_CALLBACK(pl_progress_bar_stop_clicked), NULL);
3967
gtk_widget_show_all(pl_progress_bar_container);
3969
g_timeout_add(200, pl_progress_bar_update, NULL);
3974
playlist_progress_bar_hide(void) {
3976
if (pl_progress_bar != NULL) {
3977
gtk_widget_destroy(pl_progress_bar);
3978
pl_progress_bar = NULL;
3981
if (pl_progress_bar_stop_button != NULL) {
3982
gtk_widget_destroy(pl_progress_bar_stop_button);
3983
pl_progress_bar_stop_button = NULL;
3989
remove_files (void) {
3991
GtkWidget *info_dialog;
3992
gint response, n = 0, i = 0, j, files, last_pos;
3993
GtkTreeIter iter, iter_child;
3994
gchar temp[MAXLEN], *filename;
3998
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
4000
if (gtk_tree_selection_iter_is_selected(play_select, &iter)) {
4001
n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
4006
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, &iter, j++)) {
4007
if (gtk_tree_selection_iter_is_selected(play_select, &iter_child)) {
4017
sprintf(temp, _("The selected file will be deleted from the filesystem. "
4018
"No recovery will be possible after this operation.\n\n"
4021
sprintf(temp, _("All selected files will be deleted from the filesystem. "
4022
"No recovery will be possible after this operation.\n\n"
4026
info_dialog = gtk_message_dialog_new (options.playlist_is_embedded ? GTK_WINDOW(main_window) : GTK_WINDOW(playlist_window),
4027
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
4028
GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, temp);
4030
gtk_window_set_title(GTK_WINDOW(info_dialog), _("Remove file"));
4031
gtk_widget_show (info_dialog);
4033
response = aqualung_dialog_run (GTK_DIALOG (info_dialog));
4035
gtk_widget_destroy(info_dialog);
4037
if (response == GTK_RESPONSE_YES) {
4040
while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
4041
if (gtk_tree_selection_iter_is_selected(play_select, &iter)) {
4043
char * filename_locale;
4045
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_PHYSICAL_FILENAME, &filename, -1);
4047
if ((filename_locale = g_locale_from_utf8(filename, -1, NULL, NULL, NULL)) == NULL) {
4052
n = unlink(filename_locale);
4054
gtk_tree_store_remove(play_store, &iter);
4056
last_pos = (i-1) < 0 ? 0 : i-1;
4059
g_free(filename_locale);
4064
for (i = 0; gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i); i++);
4067
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, last_pos);
4068
set_cursor_in_playlist(&iter, FALSE);
4070
playlist_content_changed();
4076
set_cursor_in_playlist (GtkTreeIter *iter, gboolean scroll) {
4078
GtkTreePath *visible_path;
4080
visible_path = gtk_tree_model_get_path (GTK_TREE_MODEL(play_store), iter);
4083
gtk_tree_view_set_cursor (GTK_TREE_VIEW (play_list), visible_path, NULL, TRUE);
4084
if (scroll == TRUE) {
4085
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (play_list), visible_path,
4086
NULL, TRUE, 0.3, 0.0);
4088
gtk_tree_path_free(visible_path);
4093
select_active_position_in_playlist(void) {
4098
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(play_store), &iter)) {
4101
gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_SELECTION_COLOR, &str, -1);
4102
if (strcmp(str, pl_color_active) == 0) {
4103
set_cursor_in_playlist(&iter, FALSE);
4108
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(play_store), &iter));
4112
// vim: shiftwidth=8:tabstop=8:softtabstop=8 :