~ubuntu-branches/ubuntu/gutsy/aqualung/gutsy

« back to all changes in this revision

Viewing changes to src/playlist.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Cécile (Le_Vert)
  • Date: 2007-03-10 16:07:32 UTC
  • Revision ID: james.westby@ubuntu.com-20070310160732-6hkwtb2d1h9sx6nr
Tags: upstream-0.9~beta7.1
Import upstream version 0.9~beta7.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 585 2007-02-12 11:18:05Z peterszilagyi $
 
19
*/
 
20
 
 
21
 
 
22
#include <config.h>
 
23
 
 
24
#include <stdio.h>
 
25
#include <string.h>
 
26
#include <dirent.h>
 
27
#include <unistd.h>
 
28
#include <sys/stat.h>
 
29
#include <math.h>
 
30
#include <gtk/gtk.h>
 
31
#include <gdk/gdkkeysyms.h>
 
32
#include <glib.h>
 
33
#include <glib/gstdio.h>
 
34
#include <libxml/xmlmemory.h>
 
35
#include <libxml/parser.h>
 
36
 
 
37
#include "common.h"
 
38
#include "core.h"
 
39
#include "rb.h"
 
40
#include "cdda.h"
 
41
#include "gui_main.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"
 
47
#include "volume.h"
 
48
#include "options.h"
 
49
#include "i18n.h"
 
50
#include "search_playlist.h"
 
51
#include "playlist.h"
 
52
#include "ifp_device.h"
 
53
 
 
54
extern options_t options;
 
55
 
 
56
extern void assign_audio_fc_filters(GtkFileChooser *fc);
 
57
extern void assign_playlist_fc_filters(GtkFileChooser *fc);
 
58
 
 
59
extern char pl_color_active[14];
 
60
extern char pl_color_inactive[14];
 
61
 
 
62
extern GtkTooltips * aqualung_tooltips;
 
63
 
 
64
extern GtkWidget* gui_stock_label_button(gchar *blabel, const gchar *bstock);
 
65
 
 
66
extern PangoFontDescription *fd_playlist;
 
67
extern PangoFontDescription *fd_statusbar;
 
68
 
 
69
GtkWidget * playlist_window;
 
70
GtkWidget * da_dialog;
 
71
GtkWidget * scrolled_win;
 
72
 
 
73
extern GtkWidget * main_window;
 
74
extern GtkWidget * info_window;
 
75
extern GtkWidget * vol_window;
 
76
 
 
77
extern GtkTreeSelection * music_select;
 
78
 
 
79
gint playlist_color_is_set;
 
80
 
 
81
gint playlist_drag_y;
 
82
gint playlist_scroll_up_tag = -1;
 
83
gint playlist_scroll_dn_tag = -1;
 
84
 
 
85
 
 
86
extern gulong play_id;
 
87
extern gulong pause_id;
 
88
extern GtkWidget * play_button;
 
89
extern GtkWidget * pause_button;
 
90
 
 
91
extern int vol_finished;
 
92
extern int vol_index;
 
93
gint vol_n_tracks;
 
94
gint vol_is_average;
 
95
vol_queue_t * pl_vol_queue;
 
96
 
 
97
/* used to store array of tree iters of tracks selected for RVA calc */
 
98
GtkTreeIter * vol_iters;
 
99
 
 
100
 
 
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;
 
110
 
 
111
GtkWidget * statusbar_total;
 
112
GtkWidget * statusbar_total_label;
 
113
GtkWidget * statusbar_selected;
 
114
GtkWidget * statusbar_selected_label;
 
115
 
 
116
volatile int playlist_thread_stop; 
 
117
int pl_progress_bar_semaphore;
 
118
 
 
119
GtkWidget * pl_progress_bar;
 
120
GtkWidget * pl_progress_bar_container;
 
121
GtkWidget * pl_progress_bar_stop_button;
 
122
 
 
123
volatile int playlist_data_written;
 
124
 
 
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)
 
129
 
 
130
 
 
131
/* popup menus */
 
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;
 
157
 
 
158
#ifdef HAVE_IFP
 
159
GtkWidget * plist__send_songs_to_iriver;
 
160
GtkWidget * plist__separator3;
 
161
#endif /* HAVE_IFP */
 
162
 
 
163
gchar command[RB_CONTROL_SIZE];
 
164
 
 
165
gchar fileinfo_name[MAXLEN];
 
166
gchar fileinfo_file[MAXLEN];
 
167
 
 
168
extern int is_file_loaded;
 
169
extern int is_paused;
 
170
extern int allow_seeks;
 
171
 
 
172
extern char current_file[MAXLEN];
 
173
 
 
174
extern rb_t * rb_gui2disk;
 
175
 
 
176
extern GtkWidget * playlist_toggle;
 
177
 
 
178
 
 
179
typedef struct _playlist_filemeta {
 
180
        char * title;
 
181
        char * filename;
 
182
        float duration;
 
183
        float voladj;
 
184
        int active;
 
185
        int level;
 
186
} playlist_filemeta;
 
187
 
 
188
 
 
189
playlist_filemeta * playlist_filemeta_get(char * physical_name, char * alt_name, int composit);
 
190
void playlist_filemeta_free(playlist_filemeta * plfm);
 
191
 
 
192
void remove_files (void);
 
193
void set_cursor_in_playlist (GtkTreeIter *iter, gboolean scroll);
 
194
void select_active_position_in_playlist(void);
 
195
 
 
196
void playlist_selection_changed(GtkTreeSelection * sel, gpointer data);
 
197
 
 
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);
 
206
 
 
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);
 
213
 
 
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);
 
218
 
 
219
 
 
220
 
 
221
void
 
222
voladj2str(float voladj, char * str) {
 
223
 
 
224
        if (fabs(voladj) < 0.05f) {
 
225
                sprintf(str, " %.1f dB", 0.0f);
 
226
        } else {
 
227
                if (voladj >= 0.0f) {
 
228
                        sprintf(str, " %.1f dB", voladj);
 
229
                } else {
 
230
                        sprintf(str, "%.1f dB", voladj);
 
231
                }
 
232
        }
 
233
}
 
234
 
 
235
void
 
236
disable_bold_font_in_playlist() {
 
237
 
 
238
        GtkTreeIter iter;
 
239
        gint i = 0;
 
240
 
 
241
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
242
                gint j = 0;
 
243
                GtkTreeIter iter_child;
 
244
 
 
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);
 
248
                }
 
249
        }
 
250
}
 
251
 
 
252
 
 
253
void
 
254
adjust_playlist_item_color(GtkTreeIter * piter, char * active, char * inactive) {
 
255
 
 
256
        gchar * str;
 
257
 
 
258
        gtk_tree_model_get(GTK_TREE_MODEL(play_store), piter, COLUMN_SELECTION_COLOR, &str, -1);
 
259
        
 
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);
 
264
                }
 
265
        }
 
266
        
 
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);
 
271
        }
 
272
 
 
273
        g_free(str);
 
274
}
 
275
 
 
276
 
 
277
void
 
278
set_playlist_color() {
 
279
        
 
280
        GdkColor color;
 
281
        GtkTreeIter iter;
 
282
        gchar active[14];
 
283
        gchar inactive[14];
 
284
        gint i = 0;
 
285
 
 
286
        if (options.override_skin_settings && (gdk_color_parse(options.activesong_color, &color) == TRUE)) {
 
287
 
 
288
                if (!color.red && !color.green && !color.blue) {
 
289
                        color.red++;
 
290
                }
 
291
 
 
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;
 
295
        }
 
296
 
 
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);
 
301
 
 
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);
 
306
 
 
307
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
308
                gint j = 0;
 
309
                GtkTreeIter iter_child;
 
310
 
 
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);
 
314
                }
 
315
        }
 
316
 
 
317
        strcpy(pl_color_active, active);
 
318
        strcpy(pl_color_inactive, inactive);        
 
319
}
 
320
 
 
321
 
 
322
/* Calls foreach on each selected track iter. A track is selected iff
 
323
   it is selected or its parent album node is selected. */
 
324
 
 
325
void
 
326
playlist_foreach_selected(void (* foreach)(GtkTreeIter *, void *), void * data) {
 
327
 
 
328
        GtkTreeIter iter_top;
 
329
        GtkTreeIter iter;
 
330
        gint i;
 
331
        gint j;
 
332
 
 
333
        i = 0;
 
334
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_top, NULL, i++)) {
 
335
 
 
336
                gboolean topsel = gtk_tree_selection_iter_is_selected(play_select, &iter_top);
 
337
 
 
338
                if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(play_store), &iter_top)) {
 
339
 
 
340
                        j = 0;
 
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);
 
344
                                }
 
345
                        }
 
346
 
 
347
                } else if (topsel) {
 
348
                        (*foreach)(&iter_top, data);
 
349
                }
 
350
        }
 
351
}
 
352
 
 
353
 
 
354
GtkTreePath *
 
355
get_playing_path(void) {
 
356
 
 
357
        GtkTreeIter iter;
 
358
        gchar * color;
 
359
        int i = 0;
 
360
 
 
361
 
 
362
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
363
 
 
364
                gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_SELECTION_COLOR, &color, -1);
 
365
 
 
366
                if (strcmp(color, pl_color_active) == 0) {
 
367
                        if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) > 0) {
 
368
 
 
369
                                GtkTreeIter iter_child;
 
370
                                gchar * color_child;
 
371
                                int j = 0;
 
372
 
 
373
                                while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store),
 
374
                                                                     &iter_child, &iter, j++)) {
 
375
 
 
376
                                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter_child,
 
377
                                                           COLUMN_SELECTION_COLOR, &color_child, -1);
 
378
 
 
379
                                        if (strcmp(color_child, pl_color_active) == 0) {
 
380
                                                g_free(color);
 
381
                                                g_free(color_child);
 
382
                                                return gtk_tree_model_get_path(GTK_TREE_MODEL(play_store), &iter_child);
 
383
                                        }
 
384
 
 
385
                                        g_free(color_child);
 
386
                                }
 
387
                        } else {
 
388
                                g_free(color);
 
389
                                return gtk_tree_model_get_path(GTK_TREE_MODEL(play_store), &iter);
 
390
                        }
 
391
                }
 
392
 
 
393
                g_free(color);
 
394
        }
 
395
 
 
396
        return NULL;
 
397
}
 
398
 
 
399
 
 
400
void 
 
401
start_playback_from_playlist(GtkTreePath * path) {
 
402
 
 
403
        GtkTreeIter iter;
 
404
        GtkTreePath * p;
 
405
        gchar cmd;
 
406
        cue_t cue;
 
407
        
 
408
        if (!allow_seeks)
 
409
                return;
 
410
 
 
411
        p = get_playing_path();
 
412
        if (p != NULL) {
 
413
                gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, p);
 
414
                gtk_tree_path_free(p);
 
415
                unmark_track(&iter);
 
416
        }
 
417
        
 
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);
 
423
        } else {
 
424
                mark_track(&iter);
 
425
        }
 
426
        
 
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);
 
431
 
 
432
        if (options.show_sn_title) {
 
433
                refresh_displays();
 
434
        }
 
435
 
 
436
        toggle_noeffect(PLAY, TRUE);
 
437
 
 
438
        if (is_paused) {
 
439
                is_paused = 0;
 
440
                toggle_noeffect(PAUSE, FALSE);
 
441
        }
 
442
                
 
443
        cmd = CMD_CUE;
 
444
        rb_write(rb_gui2disk, &cmd, sizeof(char));
 
445
        rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
 
446
        try_waking_disk_thread();
 
447
        
 
448
        is_file_loaded = 1;
 
449
}
 
450
 
 
451
 
 
452
static gboolean
 
453
playlist_window_close(GtkWidget * widget, GdkEvent * event, gpointer data) {
 
454
 
 
455
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_toggle), FALSE);
 
456
 
 
457
        return TRUE;
 
458
}
 
459
 
 
460
 
 
461
gint
 
462
playlist_window_key_pressed(GtkWidget * widget, GdkEventKey * kevent) {
 
463
 
 
464
        GtkTreePath * path;
 
465
        GtkTreeViewColumn * column;
 
466
        GtkTreeIter iter;
 
467
        gchar * pname;
 
468
        gchar * pfile;
 
469
 
 
470
 
 
471
        switch (kevent->keyval) {
 
472
 
 
473
        case GDK_Insert:
 
474
        case GDK_KP_Insert:
 
475
                if (kevent->state & GDK_SHIFT_MASK) {  /* SHIFT + Insert */
 
476
                        add_directory(NULL, NULL);
 
477
                } else {
 
478
                        add_files(NULL, NULL);
 
479
                }
 
480
                return TRUE;
 
481
        case GDK_q:
 
482
        case GDK_Q:
 
483
        case GDK_Escape:
 
484
                if (!options.playlist_is_embedded) {
 
485
                        playlist_window_close(NULL, NULL, NULL);
 
486
                }
 
487
                return TRUE;
 
488
        case GDK_F1:
 
489
        case GDK_i:
 
490
        case GDK_I:
 
491
                gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &path, &column);
 
492
 
 
493
                if (path &&
 
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)) {
 
496
 
 
497
                        GtkTreeIter dummy;
 
498
                        
 
499
                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
 
500
                                           COLUMN_TRACK_NAME, &pname, COLUMN_PHYSICAL_FILENAME, &pfile, -1);
 
501
                        
 
502
                        strncpy(fileinfo_name, pname, MAXLEN-1);
 
503
                        strncpy(fileinfo_file, pfile, MAXLEN-1);
 
504
                        free(pname);
 
505
                        free(pfile);
 
506
                        show_file_info(fileinfo_name, fileinfo_file, 0, NULL, dummy);
 
507
                }
 
508
                return TRUE;
 
509
        case GDK_Return:
 
510
        case GDK_KP_Enter:
 
511
                gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &path, &column);
 
512
 
 
513
                if (path) {
 
514
                        start_playback_from_playlist(path);
 
515
                }               
 
516
                return TRUE;
 
517
        case GDK_u:
 
518
        case GDK_U:
 
519
                cut__sel_cb(NULL);
 
520
                return TRUE;
 
521
        case GDK_f:
 
522
        case GDK_F:
 
523
                plist__search_cb(NULL);
 
524
                return TRUE;
 
525
        case GDK_a:
 
526
        case GDK_A:
 
527
                if (kevent->state & GDK_CONTROL_MASK) {
 
528
                        if (kevent->state & GDK_SHIFT_MASK) {
 
529
                                sel__none_cb(NULL);
 
530
                        } else {
 
531
                                sel__all_cb(NULL);
 
532
                        }
 
533
                } else {
 
534
                        show_active_position_in_playlist_toggle();
 
535
                }
 
536
                return TRUE;
 
537
        case GDK_w:
 
538
        case GDK_W:
 
539
                gtk_tree_view_collapse_all(GTK_TREE_VIEW(play_list));  
 
540
                show_active_position_in_playlist();
 
541
                return TRUE;
 
542
        case GDK_Delete:
 
543
        case GDK_KP_Delete:
 
544
                if (kevent->state & GDK_SHIFT_MASK) {  /* SHIFT + Delete */
 
545
                        rem__all_cb(NULL);
 
546
                } else if (kevent->state & GDK_CONTROL_MASK) {  /* CTRL + Delete */
 
547
                        remove_files();
 
548
                } else {
 
549
                        rem__sel_cb(NULL);
 
550
                }
 
551
                return TRUE;
 
552
#ifdef HAVE_IFP
 
553
        case GDK_t:
 
554
        case GDK_T:
 
555
                aifp_transfer_files();
 
556
                return TRUE;
 
557
                break;
 
558
#endif /* HAVE_IFP */
 
559
 
 
560
        case GDK_grave:
 
561
                expand_collapse_album_node();
 
562
                return TRUE;
 
563
        }
 
564
 
 
565
        return FALSE;
 
566
}
 
567
 
 
568
 
 
569
gint
 
570
doubleclick_handler(GtkWidget * widget, GdkEventButton * event, gpointer func_data) {
 
571
 
 
572
        GtkTreePath * path;
 
573
        GtkTreeViewColumn * column;
 
574
        GtkTreeIter iter;
 
575
        gchar * pname;
 
576
        gchar * pfile;
 
577
 
 
578
        if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
 
579
                
 
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);
 
583
                }
 
584
        }
 
585
 
 
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)) {
 
591
 
 
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);
 
594
 
 
595
                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
 
596
                                           COLUMN_TRACK_NAME, &pname, COLUMN_PHYSICAL_FILENAME, &pfile, -1);
 
597
                                        
 
598
                        strncpy(fileinfo_name, pname, MAXLEN-1);
 
599
                        strncpy(fileinfo_file, pfile, MAXLEN-1);
 
600
                        free(pname);
 
601
                        free(pfile);
 
602
                                        
 
603
                        gtk_widget_set_sensitive(plist__fileinfo, TRUE);
 
604
                } else {
 
605
                        gtk_widget_set_sensitive(plist__fileinfo, FALSE);
 
606
                }
 
607
 
 
608
                gtk_widget_set_sensitive(plist__rva, (vol_window == NULL) ? TRUE : FALSE);
 
609
 
 
610
                gtk_menu_popup(GTK_MENU(plist_menu), NULL, NULL, NULL, NULL,
 
611
                               event->button, event->time);
 
612
                return TRUE;
 
613
        }
 
614
        return FALSE;
 
615
}
 
616
 
 
617
 
 
618
static int
 
619
filter(const struct dirent * de) {
 
620
 
 
621
        return de->d_name[0] != '.';
 
622
}
 
623
 
 
624
 
 
625
gboolean
 
626
finalize_add_to_playlist(gpointer data) {
 
627
 
 
628
        --pl_progress_bar_semaphore;
 
629
 
 
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();
 
635
        }
 
636
 
 
637
        return FALSE;
 
638
}
 
639
 
 
640
 
 
641
/* if data == NULL clears playlist */
 
642
gboolean
 
643
add_file_to_playlist(gpointer data) {
 
644
 
 
645
        playlist_filemeta * plfm = (playlist_filemeta *)data;
 
646
 
 
647
 
 
648
        AQUALUNG_MUTEX_LOCK(playlist_wait_mutex)
 
649
 
 
650
        if (plfm == NULL) {
 
651
                rem__all_cb(NULL);
 
652
        } else {
 
653
                GtkTreeIter iter;
 
654
                GtkTreePath * path;
 
655
                gchar voladj_str[32];
 
656
                gchar duration_str[MAXLEN];
 
657
 
 
658
 
 
659
                voladj2str(plfm->voladj, voladj_str);
 
660
                time2time_na(plfm->duration, duration_str);
 
661
 
 
662
                if (plfm->level == 0) {
 
663
 
 
664
                        /* deactivate toplevel item if playing path already exists */
 
665
                        if (plfm->active && (path = get_playing_path()) != NULL) {
 
666
                                plfm->active = 0;
 
667
                                gtk_tree_path_free(path);
 
668
                        }
 
669
 
 
670
                        gtk_tree_store_append(play_store, &iter, NULL);
 
671
                } else {
 
672
                        GtkTreeIter parent;
 
673
                        int n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), NULL);
 
674
 
 
675
                        if (n == 0) {
 
676
                                /* someone viciously cleared the list while adding tracks to album node;
 
677
                                   ignore further tracks to this node */
 
678
                                goto finish;
 
679
                        }
 
680
 
 
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);
 
683
                }
 
684
 
 
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, 
 
694
                           -1);
 
695
        }
 
696
 
 
697
 finish:
 
698
        if (plfm != NULL) {
 
699
                playlist_filemeta_free(plfm);
 
700
        }
 
701
 
 
702
        --playlist_data_written;
 
703
 
 
704
        if (playlist_data_written == 0) {
 
705
                AQUALUNG_COND_SIGNAL(playlist_thread_wait)
 
706
        }
 
707
 
 
708
        AQUALUNG_MUTEX_UNLOCK(playlist_wait_mutex)
 
709
 
 
710
        return FALSE;
 
711
}
 
712
 
 
713
 
 
714
void
 
715
playlist_thread_add_to_list(playlist_filemeta * plfm) {
 
716
 
 
717
        AQUALUNG_MUTEX_LOCK(playlist_wait_mutex)
 
718
        ++playlist_data_written;
 
719
 
 
720
        g_idle_add(add_file_to_playlist, (gpointer)plfm);
 
721
 
 
722
        if (playlist_data_written > 100) {
 
723
                AQUALUNG_COND_WAIT(playlist_thread_wait, playlist_wait_mutex)
 
724
        }
 
725
 
 
726
        AQUALUNG_MUTEX_UNLOCK(playlist_wait_mutex)
 
727
}
 
728
 
 
729
 
 
730
void *
 
731
add_files_to_playlist_thread(void * arg) {
 
732
 
 
733
        GSList * lfiles;
 
734
        GSList * node;
 
735
 
 
736
        AQUALUNG_THREAD_DETACH()
 
737
 
 
738
        AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
 
739
 
 
740
        lfiles = (GSList *)arg;
 
741
 
 
742
        for (node = lfiles; node; node = node->next) {
 
743
 
 
744
                if (!playlist_thread_stop) {
 
745
                        playlist_filemeta * plfm = NULL;
 
746
 
 
747
                        if ((plfm = playlist_filemeta_get((char *)node->data, NULL, 1)) != NULL) {
 
748
 
 
749
                                playlist_thread_add_to_list(plfm);
 
750
                        }
 
751
                }
 
752
 
 
753
                g_free(node->data);
 
754
        }
 
755
 
 
756
        g_slist_free(lfiles);
 
757
 
 
758
        g_idle_add(finalize_add_to_playlist, NULL);
 
759
 
 
760
        AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
 
761
 
 
762
        return NULL;
 
763
}
 
764
 
 
765
 
 
766
void
 
767
add_files(GtkWidget * widget, gpointer data) {
 
768
 
 
769
        GtkWidget *dialog;
 
770
 
 
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, 
 
776
                                             NULL);
 
777
 
 
778
        deflicker();
 
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);
 
785
 
 
786
        if (options.show_hidden) {
 
787
                gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
 
788
        }
 
789
 
 
790
        if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
 
791
 
 
792
                GSList *lfiles;
 
793
 
 
794
                strncpy(options.currdir, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)),
 
795
                        MAXLEN-1);
 
796
 
 
797
                lfiles = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
 
798
 
 
799
                playlist_progress_bar_show();
 
800
                AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, add_files_to_playlist_thread, lfiles)
 
801
        }
 
802
 
 
803
        gtk_widget_destroy(dialog);
 
804
}
 
805
 
 
806
 
 
807
void
 
808
add_dir_to_playlist(char * dirname) {
 
809
 
 
810
        gint i, n;
 
811
        struct dirent ** ent;
 
812
        struct stat st_file;
 
813
        gchar path[MAXLEN];
 
814
 
 
815
 
 
816
        if (playlist_thread_stop) {
 
817
                return;
 
818
        }
 
819
 
 
820
        n = scandir(dirname, &ent, filter, alphasort);
 
821
        for (i = 0; i < n; i++) {
 
822
 
 
823
                if (playlist_thread_stop) {
 
824
                        break;
 
825
                }
 
826
 
 
827
                snprintf(path, MAXLEN-1, "%s/%s", dirname, ent[i]->d_name);
 
828
 
 
829
                if (stat(path, &st_file) == -1) {
 
830
                        free(ent[i]);
 
831
                        continue;
 
832
                }
 
833
 
 
834
                if (S_ISDIR(st_file.st_mode)) {
 
835
                        add_dir_to_playlist(path);
 
836
                } else {
 
837
                        playlist_filemeta * plfm = NULL;
 
838
 
 
839
                        if ((plfm = playlist_filemeta_get(path, NULL, 1)) != NULL) {
 
840
 
 
841
                                playlist_thread_add_to_list(plfm);
 
842
                        }
 
843
                }
 
844
 
 
845
                free(ent[i]);
 
846
        }
 
847
 
 
848
        while (i < n) {
 
849
                free(ent[i]);
 
850
                ++i;
 
851
        }
 
852
 
 
853
        if (n > 0) {
 
854
                free(ent);
 
855
        }
 
856
}
 
857
 
 
858
 
 
859
void *
 
860
add_dir_to_playlist_thread(void * arg) {
 
861
 
 
862
 
 
863
        GSList * lfiles;
 
864
        GSList * node;
 
865
 
 
866
        AQUALUNG_THREAD_DETACH()
 
867
 
 
868
        AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
 
869
 
 
870
        lfiles = (GSList *)arg;
 
871
 
 
872
        for (node = lfiles; node; node = node->next) {
 
873
 
 
874
                add_dir_to_playlist((char *)node->data);
 
875
                g_free(node->data);
 
876
        }
 
877
 
 
878
        g_slist_free(lfiles);
 
879
 
 
880
        g_idle_add(finalize_add_to_playlist, NULL);
 
881
 
 
882
        AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
 
883
 
 
884
        return NULL;
 
885
}
 
886
 
 
887
 
 
888
void
 
889
add_directory(GtkWidget * widget, gpointer data) {
 
890
 
 
891
        GtkWidget *dialog;
 
892
 
 
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, 
 
898
                                             NULL);
 
899
 
 
900
        deflicker();
 
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);
 
906
 
 
907
        if (options.show_hidden) {
 
908
                gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
 
909
        }
 
910
 
 
911
        if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
 
912
 
 
913
                GSList *lfiles;
 
914
 
 
915
                strncpy(options.currdir, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)),
 
916
                        MAXLEN-1);
 
917
 
 
918
                lfiles = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
 
919
 
 
920
                playlist_progress_bar_show();
 
921
                AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, add_dir_to_playlist_thread, lfiles)
 
922
        }
 
923
 
 
924
        gtk_widget_destroy(dialog);
 
925
}
 
926
 
 
927
 
 
928
void
 
929
plist__save_cb(gpointer data) {
 
930
 
 
931
        GtkWidget * dialog;
 
932
        gchar * selected_filename;
 
933
 
 
934
 
 
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, 
 
940
                                             NULL);
 
941
 
 
942
        deflicker();
 
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");
 
945
 
 
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);
 
949
 
 
950
        if (options.show_hidden) {
 
951
                gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
 
952
        }
 
953
 
 
954
        if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
 
955
 
 
956
                selected_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 
957
                strncpy(options.currdir, selected_filename, MAXLEN-1);
 
958
 
 
959
                save_playlist(selected_filename);
 
960
 
 
961
                g_free(selected_filename);
 
962
        }
 
963
 
 
964
        gtk_widget_destroy(dialog);
 
965
}
 
966
 
 
967
 
 
968
void *
 
969
playlist_load_thread(void * arg) {
 
970
 
 
971
        char * filename = (char *)arg;
 
972
        char fullname[MAXLEN];
 
973
        playlist_filemeta * plfm = NULL;
 
974
 
 
975
 
 
976
        AQUALUNG_THREAD_DETACH()
 
977
 
 
978
        AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
 
979
 
 
980
        if (!filename) {
 
981
                return NULL;
 
982
        }
 
983
 
 
984
        normalize_filename(filename, fullname);
 
985
        free(filename);
 
986
 
 
987
        switch (is_playlist(fullname)) {
 
988
        case 0:
 
989
                if ((plfm = playlist_filemeta_get(fullname, NULL, 1)) == NULL) {
 
990
                        fprintf(stderr, "playlist_load_thread(): playlist_filemeta_get() returned NULL\n");
 
991
                } else {
 
992
                        playlist_thread_add_to_list(NULL);
 
993
                        playlist_thread_add_to_list(plfm);
 
994
                }
 
995
                break;
 
996
        case 1:
 
997
                load_playlist(fullname, 0);
 
998
                break;
 
999
        case 2:
 
1000
                load_m3u(fullname, 0);
 
1001
                break;
 
1002
        case 3:
 
1003
                load_pls(fullname, 0);
 
1004
                break;
 
1005
        }
 
1006
 
 
1007
        g_idle_add(finalize_add_to_playlist, NULL);
 
1008
 
 
1009
        AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
 
1010
 
 
1011
        return NULL;
 
1012
}
 
1013
 
 
1014
 
 
1015
void
 
1016
plist__load_cb(gpointer data) {
 
1017
 
 
1018
        GtkWidget * dialog;
 
1019
        const gchar * selected_filename;
 
1020
 
 
1021
 
 
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, 
 
1027
                                             NULL);
 
1028
 
 
1029
        deflicker();
 
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);
 
1035
 
 
1036
        if (options.show_hidden) {
 
1037
                gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
 
1038
        }
 
1039
 
 
1040
        if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
 
1041
 
 
1042
                selected_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 
1043
                strncpy(options.currdir, selected_filename, MAXLEN-1);
 
1044
 
 
1045
                playlist_progress_bar_show();
 
1046
 
 
1047
                AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, playlist_load_thread, strdup(selected_filename))
 
1048
        }
 
1049
 
 
1050
        gtk_widget_destroy(dialog);
 
1051
}
 
1052
 
 
1053
 
 
1054
void *
 
1055
playlist_enqueue_thread(void * arg) {
 
1056
 
 
1057
        char * filename = (char *)arg;
 
1058
        char fullname[MAXLEN];
 
1059
        playlist_filemeta * plfm = NULL;
 
1060
 
 
1061
 
 
1062
        AQUALUNG_THREAD_DETACH()
 
1063
 
 
1064
        AQUALUNG_MUTEX_LOCK(playlist_thread_mutex)
 
1065
 
 
1066
        if (!filename) {
 
1067
                return NULL;
 
1068
        }
 
1069
 
 
1070
        normalize_filename(filename, fullname);
 
1071
        free(filename);
 
1072
 
 
1073
        switch (is_playlist(fullname)) {
 
1074
        case 0:
 
1075
                if ((plfm = playlist_filemeta_get(fullname, NULL, 1)) == NULL) {
 
1076
                        fprintf(stderr, "playlist_enqueue_thread(): playlist_filemeta_get() returned NULL\n");
 
1077
                } else {
 
1078
                        playlist_thread_add_to_list(plfm);
 
1079
                }
 
1080
                break;
 
1081
        case 1:
 
1082
                load_playlist(fullname, 1);
 
1083
                break;
 
1084
        case 2:
 
1085
                load_m3u(fullname, 1);
 
1086
                break;
 
1087
        case 3:
 
1088
                load_pls(fullname, 1);
 
1089
                break;
 
1090
        }
 
1091
 
 
1092
        g_idle_add(finalize_add_to_playlist, NULL);
 
1093
 
 
1094
        AQUALUNG_MUTEX_UNLOCK(playlist_thread_mutex)
 
1095
 
 
1096
        return NULL;
 
1097
}
 
1098
 
 
1099
 
 
1100
void
 
1101
plist__enqueue_cb(gpointer data) {
 
1102
 
 
1103
        GtkWidget * dialog;
 
1104
        const gchar * selected_filename;
 
1105
 
 
1106
 
 
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, 
 
1112
                                             NULL);
 
1113
 
 
1114
        deflicker();
 
1115
        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), options.currdir);
 
1116
        assign_playlist_fc_filters(GTK_FILE_CHOOSER(dialog));
 
1117
 
 
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);
 
1121
 
 
1122
        if (options.show_hidden) {
 
1123
                gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
 
1124
        }
 
1125
 
 
1126
        if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
 
1127
 
 
1128
                selected_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 
1129
                strncpy(options.currdir, selected_filename, MAXLEN-1);
 
1130
 
 
1131
                playlist_progress_bar_show();
 
1132
                AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL, playlist_enqueue_thread, strdup(selected_filename))
 
1133
        }
 
1134
 
 
1135
        gtk_widget_destroy(dialog);
 
1136
}
 
1137
 
 
1138
 
 
1139
gint
 
1140
watch_vol_calc(gpointer data) {
 
1141
 
 
1142
        gfloat * volumes = (gfloat *)data;
 
1143
 
 
1144
        if (!vol_finished) {
 
1145
                return TRUE;
 
1146
        }
 
1147
 
 
1148
        if (vol_index != vol_n_tracks) {
 
1149
                free(volumes);
 
1150
                volumes = NULL;
 
1151
                return FALSE;
 
1152
        }
 
1153
 
 
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,
 
1160
                                                         options.rva_refvol,
 
1161
                                                         options.rva_steepness);
 
1162
                gint i;
 
1163
 
 
1164
                voladj2str(voladj, voladj_str);
 
1165
 
 
1166
                for (i = 0; i < vol_n_tracks; i++) {
 
1167
 
 
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);
 
1171
                        }
 
1172
                }
 
1173
        } else {
 
1174
                gfloat voladj;
 
1175
                gchar voladj_str[32];
 
1176
 
 
1177
                gint i;
 
1178
 
 
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],
 
1182
                                                         options.rva_refvol,
 
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);
 
1187
                        }
 
1188
                }
 
1189
        }
 
1190
 
 
1191
        free(volumes);
 
1192
        volumes = NULL;
 
1193
        free(vol_iters);
 
1194
        vol_iters = NULL;
 
1195
        return FALSE;
 
1196
}
 
1197
 
 
1198
 
 
1199
void
 
1200
plist_setup_vol_foreach(GtkTreeIter * iter, void * data) {
 
1201
 
 
1202
        gchar * pfile;
 
1203
 
 
1204
        gtk_tree_model_get(GTK_TREE_MODEL(play_store), iter, 1, &pfile, -1);
 
1205
 
 
1206
        if (pl_vol_queue == NULL) {
 
1207
                pl_vol_queue = vol_queue_push(NULL, pfile, *iter/*dummy*/);
 
1208
        } else {
 
1209
                vol_queue_push(pl_vol_queue, pfile, *iter/*dummy*/);
 
1210
        }
 
1211
        ++vol_n_tracks;
 
1212
 
 
1213
        vol_iters = (GtkTreeIter *)realloc(vol_iters, vol_n_tracks * sizeof(GtkTreeIter));
 
1214
        if (!vol_iters) {
 
1215
                fprintf(stderr, "realloc error in plist_setup_vol_calc()\n");
 
1216
                return;
 
1217
        }
 
1218
        vol_iters[vol_n_tracks-1] = *iter;
 
1219
 
 
1220
        g_free(pfile);
 
1221
}
 
1222
 
 
1223
void
 
1224
plist_setup_vol_calc(void) {
 
1225
 
 
1226
        gfloat * volumes = NULL;
 
1227
 
 
1228
        pl_vol_queue = NULL;
 
1229
 
 
1230
        if (vol_window != NULL) {
 
1231
                return;
 
1232
        }
 
1233
 
 
1234
        vol_n_tracks = 0;
 
1235
 
 
1236
        playlist_foreach_selected(plist_setup_vol_foreach, NULL);
 
1237
 
 
1238
        if (vol_n_tracks == 0)
 
1239
                return;
 
1240
 
 
1241
        if (!options.rva_is_enabled) {
 
1242
 
 
1243
                GtkWidget * dialog = gtk_dialog_new_with_buttons(
 
1244
                                 _("Warning"),
 
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,
 
1250
                                 NULL);
 
1251
 
 
1252
                GtkWidget * label =  gtk_label_new(_("Playback RVA is currently disabled.\n"
 
1253
                                                     "Do you want to enable it now?"));
 
1254
 
 
1255
                gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, TRUE, 10);
 
1256
                gtk_widget_show(label);
 
1257
 
 
1258
                if (aqualung_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
 
1259
                        free(vol_iters);
 
1260
                        vol_iters = NULL;
 
1261
                        gtk_widget_destroy(dialog);
 
1262
 
 
1263
                        return;
 
1264
                } else {
 
1265
                        options.rva_is_enabled = 1;
 
1266
                        gtk_widget_destroy(dialog);
 
1267
                }
 
1268
        }
 
1269
 
 
1270
        if ((volumes = calloc(vol_n_tracks, sizeof(float))) == NULL) {
 
1271
                fprintf(stderr, "calloc error in plist__rva_separate_cb()\n");
 
1272
                free(vol_iters);
 
1273
                vol_iters = NULL;
 
1274
                return;
 
1275
        }
 
1276
 
 
1277
 
 
1278
        calculate_volume(pl_vol_queue, volumes);
 
1279
        g_timeout_add(200, watch_vol_calc, (gpointer)volumes);
 
1280
}
 
1281
 
 
1282
 
 
1283
void
 
1284
plist__rva_separate_cb(gpointer data) {
 
1285
 
 
1286
        vol_is_average = 0;
 
1287
        plist_setup_vol_calc();
 
1288
}
 
1289
 
 
1290
 
 
1291
void
 
1292
plist__rva_average_cb(gpointer data) {
 
1293
 
 
1294
        vol_is_average = 1;
 
1295
        plist_setup_vol_calc();
 
1296
}
 
1297
 
 
1298
 
 
1299
void
 
1300
plist__reread_file_meta_foreach(GtkTreeIter * iter, void * data) {
 
1301
 
 
1302
        gchar * title;
 
1303
        gchar * fullname;
 
1304
        gchar voladj_str[32];
 
1305
        gchar duration_str[MAXLEN];
 
1306
        gint composit;
 
1307
        playlist_filemeta * plfm = NULL;
 
1308
        GtkTreeIter dummy;
 
1309
 
 
1310
 
 
1311
        gtk_tree_model_get(GTK_TREE_MODEL(play_store), iter,
 
1312
                           COLUMN_TRACK_NAME, &title,
 
1313
                           COLUMN_PHYSICAL_FILENAME, &fullname,
 
1314
                           -1);
 
1315
 
 
1316
        if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(play_store), &dummy, iter)) {
 
1317
                composit = 0;
 
1318
        } else {
 
1319
                composit = 1;
 
1320
        }
 
1321
 
 
1322
        plfm = playlist_filemeta_get(fullname, title, composit);
 
1323
        if (plfm == NULL) {
 
1324
                fprintf(stderr, "plist__reread_file_meta_foreach(): "
 
1325
                        "playlist_filemeta_get() returned NULL\n");
 
1326
                g_free(title);
 
1327
                g_free(fullname);
 
1328
                return;
 
1329
        }
 
1330
                        
 
1331
        voladj2str(plfm->voladj, voladj_str);
 
1332
        time2time_na(plfm->duration, duration_str);
 
1333
        
 
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,
 
1339
                           -1);
 
1340
                        
 
1341
        playlist_filemeta_free(plfm);
 
1342
        plfm = NULL;
 
1343
        g_free(title);
 
1344
        g_free(fullname);
 
1345
}
 
1346
 
 
1347
 
 
1348
void
 
1349
plist__reread_file_meta_cb(gpointer data) {
 
1350
        
 
1351
        GtkTreeIter iter;
 
1352
        GtkTreeIter iter_child;
 
1353
        gint i = 0;
 
1354
        gint j = 0;
 
1355
        gint reread = 0;
 
1356
        
 
1357
        playlist_foreach_selected(plist__reread_file_meta_foreach, NULL);
 
1358
        
 
1359
        i = 0;
 
1360
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
1361
                
 
1362
                if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(play_store), &iter)) {
 
1363
                        
 
1364
                        reread = 0;
 
1365
                        
 
1366
                        if (gtk_tree_selection_iter_is_selected(play_select, &iter)) {
 
1367
                                reread = 1;
 
1368
                        } else {
 
1369
                                j = 0;
 
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,
 
1373
                                                                                &iter_child)) {
 
1374
                                                reread = 1;
 
1375
                                                break;
 
1376
                                        }
 
1377
                                }
 
1378
                        }
 
1379
                        
 
1380
                        if (reread) {
 
1381
                                recalc_album_node(&iter);
 
1382
                        }
 
1383
                }
 
1384
        }
 
1385
        
 
1386
        delayed_playlist_rearrange(100);
 
1387
}
 
1388
 
 
1389
 
 
1390
#ifdef HAVE_IFP
 
1391
void
 
1392
plist__send_songs_to_iriver_cb(gpointer data) {
 
1393
 
 
1394
        aifp_transfer_files();
 
1395
 
 
1396
}
 
1397
#endif /* HAVE_IFP */
 
1398
 
 
1399
void
 
1400
plist__fileinfo_cb(gpointer data) {
 
1401
 
 
1402
        GtkTreeIter dummy;
 
1403
 
 
1404
        show_file_info(fileinfo_name, fileinfo_file, 0, NULL, dummy);
 
1405
}
 
1406
 
 
1407
void
 
1408
plist__search_cb(gpointer data) {
 
1409
        
 
1410
        search_playlist_dialog();
 
1411
}
 
1412
 
 
1413
 
 
1414
static gboolean
 
1415
add_cb(GtkWidget * widget, GdkEvent * event) {
 
1416
 
 
1417
        if (event->type == GDK_BUTTON_PRESS) {
 
1418
                GdkEventButton * bevent = (GdkEventButton *) event;
 
1419
 
 
1420
                if (bevent->button == 3) {
 
1421
 
 
1422
                        gtk_menu_popup(GTK_MENU(add_menu), NULL, NULL, NULL, NULL,
 
1423
                                       bevent->button, bevent->time);
 
1424
 
 
1425
                        return TRUE;
 
1426
                }
 
1427
                return FALSE;
 
1428
        }
 
1429
        return FALSE;
 
1430
}
 
1431
 
 
1432
 
 
1433
static gboolean
 
1434
sel_cb(GtkWidget * widget, GdkEvent * event) {
 
1435
 
 
1436
        if (event->type == GDK_BUTTON_PRESS) {
 
1437
                GdkEventButton * bevent = (GdkEventButton *) event;
 
1438
 
 
1439
                if (bevent->button == 3) {
 
1440
 
 
1441
                        gtk_menu_popup(GTK_MENU(sel_menu), NULL, NULL, NULL, NULL,
 
1442
                                       bevent->button, bevent->time);
 
1443
 
 
1444
                        return TRUE;
 
1445
                }
 
1446
                return FALSE;
 
1447
        }
 
1448
        return FALSE;
 
1449
}
 
1450
 
 
1451
 
 
1452
static gboolean
 
1453
rem_cb(GtkWidget * widget, GdkEvent * event) {
 
1454
 
 
1455
        if (event->type == GDK_BUTTON_PRESS) {
 
1456
                GdkEventButton * bevent = (GdkEventButton *) event;
 
1457
 
 
1458
                if (bevent->button == 3) {
 
1459
 
 
1460
                        gtk_menu_popup(GTK_MENU(rem_menu), NULL, NULL, NULL, NULL,
 
1461
                                       bevent->button, bevent->time);
 
1462
 
 
1463
                        return TRUE;
 
1464
                }
 
1465
                return FALSE;
 
1466
        }
 
1467
        return FALSE;
 
1468
}
 
1469
 
 
1470
 
 
1471
/* if alt_name != NULL, it will be used as title if no meta is found */
 
1472
playlist_filemeta *
 
1473
playlist_filemeta_get(char * physical_name, char * alt_name, int composit) {
 
1474
 
 
1475
        gchar display_name[MAXLEN];
 
1476
        gchar artist_name[MAXLEN];
 
1477
        gchar record_name[MAXLEN];
 
1478
        gchar track_name[MAXLEN];
 
1479
        metadata * meta = NULL;
 
1480
        gint use_meta = 0;
 
1481
        gchar * substr;
 
1482
 
 
1483
#if defined(HAVE_MOD) && (defined(HAVE_LIBZ) || defined(HAV_LIBBZ2))
 
1484
        gchar * pos;
 
1485
#endif /* (HAVE_MOD && (HAVE_LIBZ || HAVE_LIBBZ2)) */
 
1486
 
 
1487
#ifdef HAVE_MOD
 
1488
        if (is_valid_mod_extension(physical_name)) {
 
1489
                composit = 0;
 
1490
        }
 
1491
 
 
1492
#ifdef HAVE_LIBZ        
 
1493
        if ((pos = strrchr(physical_name, '.')) != NULL) {
 
1494
                pos++;
 
1495
 
 
1496
                if (!strcasecmp(pos, "gz")) {
 
1497
                    composit = 0;
 
1498
                }
 
1499
 
 
1500
#ifdef HAVE_LIBBZ2
 
1501
                if (!strcasecmp(pos, "bz2")) {
 
1502
                    composit = 0;
 
1503
                }
 
1504
#endif /* HAVE LIBBZ2 */
 
1505
 
 
1506
        }
 
1507
#endif /* HAVE LIBZ */
 
1508
 
 
1509
#endif /* HAVE_MOD */
 
1510
 
 
1511
        playlist_filemeta * plfm = calloc(1, sizeof(playlist_filemeta));
 
1512
        if (!plfm) {
 
1513
                fprintf(stderr, "calloc error in playlist_filemeta_get()\n");
 
1514
                return NULL;
 
1515
        }
 
1516
 
 
1517
        if ((plfm->duration = get_file_duration(physical_name)) < 0.0f) {
 
1518
                return NULL;
 
1519
        }
 
1520
 
 
1521
        if (options.rva_is_enabled) {
 
1522
                meta = meta_new();
 
1523
                if (meta != NULL && meta_read(meta, physical_name)) {
 
1524
                        if (!meta_get_rva(meta, &(plfm->voladj))) {
 
1525
                                plfm->voladj = 0.0f;
 
1526
                        }
 
1527
                } else {
 
1528
                        plfm->voladj = 0.0f;
 
1529
                }
 
1530
                if (meta != NULL) {
 
1531
                        meta_free(meta);
 
1532
                        meta = NULL;
 
1533
                }
 
1534
        } else {
 
1535
                plfm->voladj = 0.0f;
 
1536
        }
 
1537
        
 
1538
        artist_name[0] = '\0';
 
1539
        record_name[0] = '\0';
 
1540
        track_name[0] = '\0';
 
1541
 
 
1542
        if (options.auto_use_ext_meta_artist ||
 
1543
            options.auto_use_ext_meta_record ||
 
1544
            options.auto_use_ext_meta_track) {
 
1545
                
 
1546
                meta = meta_new();
 
1547
                if (meta != NULL && !meta_read(meta, physical_name)) {
 
1548
                        meta_free(meta);
 
1549
                        meta = NULL;
 
1550
                }
 
1551
        }
 
1552
        
 
1553
        use_meta = 0;
 
1554
        if ((meta != NULL) && options.auto_use_ext_meta_artist) {
 
1555
                meta_get_artist(meta, artist_name);
 
1556
                if (artist_name[0] != '\0') {
 
1557
                        use_meta = 1;
 
1558
                }
 
1559
        }
 
1560
        
 
1561
        if ((meta != NULL) && options.auto_use_ext_meta_record) {
 
1562
                meta_get_record(meta, record_name);
 
1563
                if (record_name[0] != '\0') {
 
1564
                        use_meta = 1;
 
1565
                }
 
1566
        }
 
1567
        
 
1568
        if ((meta != NULL) && options.auto_use_ext_meta_track) {
 
1569
                meta_get_title(meta, track_name);
 
1570
                if (track_name[0] != '\0') {
 
1571
                        use_meta = 1;
 
1572
                }
 
1573
        }
 
1574
        
 
1575
        if ((artist_name[0] != '\0') ||
 
1576
            (record_name[0] != '\0') ||
 
1577
            (track_name[0] != '\0')) {
 
1578
                
 
1579
                if (artist_name[0] == '\0') {
 
1580
                        strcpy(artist_name, _("Unknown"));
 
1581
                }
 
1582
                if (record_name[0] == '\0') {
 
1583
                        strcpy(record_name, _("Unknown"));
 
1584
                }
 
1585
                if (track_name[0] == '\0') {
 
1586
                        strcpy(track_name, _("Unknown"));
 
1587
                }
 
1588
        } else {
 
1589
                use_meta = 0;
 
1590
        }
 
1591
        
 
1592
        if (use_meta && (meta != NULL)) {
 
1593
                if (composit) {
 
1594
                        make_title_string(display_name, options.title_format,
 
1595
                                          artist_name, record_name, track_name);
 
1596
                } else {
 
1597
                        strncpy(display_name, track_name, MAXLEN-1);
 
1598
 
 
1599
                }
 
1600
        } else {
 
1601
                if (alt_name != NULL) {
 
1602
                        strcpy(display_name, alt_name);
 
1603
                } else {
 
1604
                        if ((substr = strrchr(physical_name, '/')) == NULL) {
 
1605
                                substr = physical_name;
 
1606
                        } else {
 
1607
                                ++substr;
 
1608
                        }
 
1609
                        substr = g_filename_display_name(substr);
 
1610
                        strcpy(display_name, substr);
 
1611
                        g_free(substr);
 
1612
                } 
 
1613
        }
 
1614
        if (meta != NULL) {
 
1615
                meta_free(meta);
 
1616
                meta = NULL;
 
1617
        }
 
1618
 
 
1619
        plfm->filename = g_strdup(physical_name);
 
1620
        plfm->title = g_strdup(display_name);
 
1621
 
 
1622
        return plfm;
 
1623
}
 
1624
 
 
1625
 
 
1626
void
 
1627
playlist_filemeta_free(playlist_filemeta * plfm) {
 
1628
 
 
1629
        free(plfm->filename);
 
1630
        free(plfm->title);
 
1631
        free(plfm);
 
1632
}
 
1633
 
 
1634
 
 
1635
void
 
1636
add__files_cb(gpointer data) {
 
1637
 
 
1638
        add_files(NULL, NULL);
 
1639
}
 
1640
 
 
1641
 
 
1642
void
 
1643
add__dir_cb(gpointer data) {
 
1644
 
 
1645
        add_directory(NULL, NULL);
 
1646
}
 
1647
 
 
1648
 
 
1649
void
 
1650
select_all(GtkWidget * widget, gpointer data) {
 
1651
 
 
1652
        gtk_tree_selection_select_all(play_select);
 
1653
}
 
1654
 
 
1655
 
 
1656
void
 
1657
sel__all_cb(gpointer data) {
 
1658
 
 
1659
        select_all(NULL, NULL);
 
1660
}
 
1661
 
 
1662
 
 
1663
void
 
1664
sel__none_cb(gpointer data) {
 
1665
 
 
1666
        gtk_tree_selection_unselect_all(play_select);
 
1667
}
 
1668
 
 
1669
 
 
1670
void
 
1671
sel__inv_foreach(GtkTreePath * path, gpointer data) {
 
1672
 
 
1673
        gtk_tree_selection_unselect_path(play_select, path);
 
1674
        gtk_tree_path_free(path);
 
1675
}
 
1676
 
 
1677
 
 
1678
void
 
1679
sel__inv_cb(gpointer data) {
 
1680
 
 
1681
        GList * list = gtk_tree_selection_get_selected_rows(play_select, NULL);
 
1682
 
 
1683
        g_signal_handlers_block_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
 
1684
 
 
1685
        gtk_tree_selection_select_all(play_select);
 
1686
        g_list_foreach(list, (GFunc)sel__inv_foreach, NULL);
 
1687
        g_list_free(list);
 
1688
 
 
1689
        g_signal_handlers_unblock_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
 
1690
 
 
1691
        playlist_selection_changed(NULL, NULL);
 
1692
}
 
1693
 
 
1694
 
 
1695
void
 
1696
rem__all_cb(gpointer data) {
 
1697
 
 
1698
        g_signal_handlers_block_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
 
1699
 
 
1700
        gtk_tree_store_clear(play_store);
 
1701
 
 
1702
        g_signal_handlers_unblock_by_func(G_OBJECT(play_select), playlist_selection_changed, NULL);
 
1703
 
 
1704
        playlist_selection_changed(NULL, NULL);
 
1705
        playlist_content_changed();
 
1706
}
 
1707
 
 
1708
void
 
1709
rem__sel_foreach(GtkTreePath * path, gpointer data) {
 
1710
 
 
1711
        GtkTreeIter iter;
 
1712
 
 
1713
        if (path != NULL) {
 
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);
 
1717
        }
 
1718
}
 
1719
 
 
1720
gint
 
1721
rem__sel_compare(gconstpointer p1, gconstpointer p2) {
 
1722
 
 
1723
        return gtk_tree_path_compare((GtkTreePath *)p1, (GtkTreePath *)p2);
 
1724
}
 
1725
 
 
1726
void
 
1727
rem__sel_save_expanded(GtkTreeView * tree_view, GtkTreePath * path, GList ** list) {
 
1728
 
 
1729
        GtkTreeIter * iter;
 
1730
 
 
1731
        if ((iter = (GtkTreeIter *)malloc(sizeof(GtkTreeIter))) == NULL) {
 
1732
                fprintf(stderr, "playlist.c: rem__sel_save_expanded(): malloc error\n");
 
1733
                return;
 
1734
        }
 
1735
 
 
1736
        gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), iter, path);
 
1737
 
 
1738
        *list = g_list_append(*list, iter);
 
1739
}
 
1740
 
 
1741
void
 
1742
rem__sel_restore_expanded(GtkTreeIter * iter, gpointer data) {
 
1743
 
 
1744
        if (gtk_tree_store_iter_is_valid(play_store, iter)) {
 
1745
 
 
1746
                GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(play_store), iter);
 
1747
 
 
1748
                gtk_tree_view_expand_row(GTK_TREE_VIEW(play_list), path, FALSE);
 
1749
                gtk_tree_path_free(path);
 
1750
        }
 
1751
 
 
1752
        free(iter);
 
1753
}
 
1754
 
 
1755
 
 
1756
void
 
1757
rem__sel_cb(gpointer data) {
 
1758
 
 
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;
 
1763
        GList * n = NULL;
 
1764
 
 
1765
        GtkTreePath * last_path;
 
1766
        GtkTreeIter last_iter;
 
1767
 
 
1768
        if (remove_list == NULL) {
 
1769
                return;
 
1770
        }
 
1771
 
 
1772
        last_path = gtk_tree_path_copy((GtkTreePath *)g_list_first(remove_list)->data);
 
1773
 
 
1774
        gtk_tree_view_map_expanded_rows(GTK_TREE_VIEW(play_list),
 
1775
                                        (GtkTreeViewMappingFunc)rem__sel_save_expanded, &expand_list);
 
1776
 
 
1777
        g_object_ref(play_store);
 
1778
 
 
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);
 
1782
 
 
1783
        remove_list = g_list_sort(remove_list, rem__sel_compare);
 
1784
        node = remove_list;
 
1785
 
 
1786
        while (node) {
 
1787
 
 
1788
                if (gtk_tree_path_get_depth((GtkTreePath *)node->data) == 1) {
 
1789
 
 
1790
                        /* if an album node is selected, it would be redundant to keep its children selected */
 
1791
 
 
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);
 
1795
                                n->data = NULL;
 
1796
                        }
 
1797
 
 
1798
                } else {
 
1799
 
 
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 */
 
1802
 
 
1803
                        GtkTreeIter iter_parent;
 
1804
                        GtkTreePath * path_parent = gtk_tree_path_copy((GtkTreePath *)node->data);
 
1805
                        int i, c;
 
1806
 
 
1807
 
 
1808
                        gtk_tree_path_up(path_parent);
 
1809
                        gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter_parent, path_parent);
 
1810
 
 
1811
                        i = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter_parent);
 
1812
                        c = i;
 
1813
 
 
1814
                        for (n = node; n && gtk_tree_path_is_ancestor(path_parent, (GtkTreePath *)n->data); n = n->next) {
 
1815
                                --i;
 
1816
                        }
 
1817
 
 
1818
                        if (i == 0) {
 
1819
                                gtk_tree_path_free(node->data);
 
1820
                                node->data = path_parent;
 
1821
                                ++i;
 
1822
 
 
1823
                                for (n = node->next; i < c; n = n->next, i++) {
 
1824
                                        gtk_tree_path_free((GtkTreePath *)n->data);
 
1825
                                        n->data = NULL;
 
1826
                                }
 
1827
                        } else {
 
1828
                                recalc_list = g_list_append(recalc_list, gtk_tree_iter_copy(&iter_parent));
 
1829
                                gtk_tree_path_free(path_parent);
 
1830
                        }
 
1831
                }
 
1832
 
 
1833
                node = n;
 
1834
        }
 
1835
 
 
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);
 
1839
 
 
1840
        g_list_foreach(recalc_list, (GFunc)recalc_album_node, NULL);
 
1841
        g_list_free(recalc_list);
 
1842
 
 
1843
        gtk_tree_view_set_model(GTK_TREE_VIEW(play_list), GTK_TREE_MODEL(play_store));
 
1844
 
 
1845
        g_list_foreach(expand_list, (GFunc)rem__sel_restore_expanded, NULL);
 
1846
        g_list_free(expand_list);
 
1847
 
 
1848
 
 
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);
 
1854
        }
 
1855
 
 
1856
        gtk_tree_path_free(last_path);
 
1857
 
 
1858
        playlist_content_changed();
 
1859
        playlist_selection_changed(NULL, NULL);
 
1860
}
 
1861
 
 
1862
 
 
1863
void
 
1864
rem__dead_cb(gpointer data) {
 
1865
 
 
1866
        GtkTreeIter iter;
 
1867
        gint i = 0;
 
1868
        gchar *filename;
 
1869
 
 
1870
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
1871
 
 
1872
                GtkTreeIter iter_child;
 
1873
                gint j = 0;
 
1874
 
 
1875
 
 
1876
                gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_PHYSICAL_FILENAME, &filename, -1);
 
1877
 
 
1878
                gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
 
1879
 
 
1880
                if (n == 0 && strstr(filename, "CDDA") != filename &&
 
1881
                    g_file_test (filename, G_FILE_TEST_IS_REGULAR) == FALSE) {
 
1882
                        g_free (filename);
 
1883
                        gtk_tree_store_remove(play_store, &iter);
 
1884
                        --i;
 
1885
                        continue;
 
1886
                }
 
1887
 
 
1888
                g_free (filename);
 
1889
 
 
1890
                if (n) {
 
1891
 
 
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);
 
1894
 
 
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);
 
1898
                                        --j;
 
1899
                                }
 
1900
 
 
1901
                                g_free (filename);
 
1902
                        }
 
1903
 
 
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);
 
1907
                                --i;
 
1908
                        } else {
 
1909
                                recalc_album_node(&iter);
 
1910
                        }
 
1911
                }
 
1912
 
 
1913
        }
 
1914
        playlist_content_changed();
 
1915
}
 
1916
 
 
1917
void
 
1918
remove_sel(GtkWidget * widget, gpointer data) {
 
1919
 
 
1920
        rem__sel_cb(NULL);
 
1921
}
 
1922
 
 
1923
 
 
1924
/* playlist item is selected -> keep, else -> remove
 
1925
 * ret: 0 if kept, 1 if removed track
 
1926
 */
 
1927
int
 
1928
cut_track_item(GtkTreeIter * piter) {
 
1929
 
 
1930
        if (!gtk_tree_selection_iter_is_selected(play_select, piter)) {
 
1931
                gtk_tree_store_remove(play_store, piter);
 
1932
                return 1;
 
1933
        }
 
1934
        return 0;
 
1935
}
 
1936
 
 
1937
 
 
1938
/* ret: 1 if at least one of album node's children are selected; else 0 */
 
1939
int
 
1940
any_tracks_selected(GtkTreeIter * piter) {
 
1941
 
 
1942
        gint j = 0;
 
1943
        GtkTreeIter iter_child;
 
1944
 
 
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)) {
 
1947
                        return 1;
 
1948
                }
 
1949
        }
 
1950
        return 0;
 
1951
}
 
1952
 
 
1953
 
 
1954
/* cut selected callback */
 
1955
void
 
1956
cut__sel_cb(gpointer data) {
 
1957
 
 
1958
        GtkTreeIter iter;
 
1959
        gint i = 0;
 
1960
 
 
1961
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
1962
 
 
1963
                gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
 
1964
 
 
1965
                if (n) { /* album node */
 
1966
                        if (any_tracks_selected(&iter)) {
 
1967
                                gint j = 0;
 
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);
 
1972
                                }
 
1973
 
 
1974
                                recalc_album_node(&iter);
 
1975
                        } else {
 
1976
                                i -= cut_track_item(&iter);
 
1977
                        }
 
1978
                } else { /* track node */
 
1979
                        i -= cut_track_item(&iter);
 
1980
                }
 
1981
        }
 
1982
        gtk_tree_selection_unselect_all(play_select);
 
1983
        playlist_content_changed();
 
1984
}
 
1985
 
 
1986
 
 
1987
gint
 
1988
playlist_rearrange_timeout_cb(gpointer data) {   
 
1989
 
 
1990
        playlist_size_allocate(NULL, NULL);
 
1991
 
 
1992
        return FALSE;
 
1993
}
 
1994
 
 
1995
 
 
1996
void
 
1997
delayed_playlist_rearrange(int delay) {
 
1998
 
 
1999
        g_timeout_add(delay, playlist_rearrange_timeout_cb, NULL);
 
2000
}
 
2001
 
 
2002
 
 
2003
gint
 
2004
playlist_size_allocate(GtkWidget * widget, GdkEventConfigure * event) {
 
2005
 
 
2006
        gint avail;
 
2007
        gint track_width;
 
2008
        gint rva_width;
 
2009
        gint length_width;
 
2010
 
 
2011
 
 
2012
        if (main_window == NULL || playlist_window == NULL) {
 
2013
                return TRUE;
 
2014
        }
 
2015
 
 
2016
        avail = play_list->allocation.width;
 
2017
 
 
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);
 
2021
        } else {
 
2022
                rva_width = 1;
 
2023
        }
 
2024
 
 
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);
 
2028
        } else {
 
2029
                length_width = 1;
 
2030
        }
 
2031
 
 
2032
        track_width = avail - rva_width - length_width;
 
2033
        if (track_width < 1)
 
2034
                track_width = 1;
 
2035
 
 
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);
 
2039
 
 
2040
 
 
2041
        if (options.playlist_is_embedded) {
 
2042
                if (main_window->window != NULL) {
 
2043
                        gtk_widget_queue_draw(main_window);
 
2044
                }
 
2045
        } else {
 
2046
                if (playlist_window->window != NULL) {
 
2047
                        gtk_widget_queue_draw(playlist_window);
 
2048
                }
 
2049
        }
 
2050
        return TRUE;
 
2051
}
 
2052
 
 
2053
 
 
2054
void
 
2055
playlist_child_stats(GtkTreeIter * iter, int * count, float * duration, float * songs_size, int selected) {
 
2056
 
 
2057
        gint j = 0;
 
2058
        gchar * tstr;
 
2059
        struct stat statbuf;
 
2060
        GtkTreeIter iter_child;
 
2061
 
 
2062
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, iter, j++)) {
 
2063
                
 
2064
                if (!selected || gtk_tree_selection_iter_is_selected(play_select, &iter_child)) {
 
2065
                        
 
2066
                        float len = 0;
 
2067
 
 
2068
                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter_child, COLUMN_DURATION, &len, 
 
2069
                                           COLUMN_PHYSICAL_FILENAME, &tstr, -1);
 
2070
                        *duration += len;
 
2071
                        (*count)++;
 
2072
                        if (stat(tstr, &statbuf) != -1) {
 
2073
                                *songs_size += statbuf.st_size / 1024.0;
 
2074
                        }
 
2075
                        g_free(tstr);
 
2076
                }
 
2077
        }
 
2078
}
 
2079
 
 
2080
 
 
2081
void
 
2082
playlist_stats_set_busy() {
 
2083
 
 
2084
        gtk_label_set_text(GTK_LABEL(statusbar_total), _("counting..."));
 
2085
}
 
2086
 
 
2087
 
 
2088
/* if selected == true -> stats for selected tracks; else: all tracks */
 
2089
void
 
2090
playlist_stats(int selected) {
 
2091
 
 
2092
        GtkTreeIter iter;
 
2093
        gint i = 0;
 
2094
 
 
2095
        gint count = 0;
 
2096
        gfloat duration = 0;
 
2097
        gfloat len = 0;
 
2098
        gchar str[MAXLEN];
 
2099
        gchar time[MAXLEN];
 
2100
        gchar * tstr;
 
2101
        gfloat songs_size, m_size;
 
2102
        struct stat statbuf;
 
2103
 
 
2104
        if (!options.enable_playlist_statusbar) {
 
2105
                return;
 
2106
        }
 
2107
 
 
2108
        if (main_window == NULL || playlist_window == NULL) {
 
2109
                return;
 
2110
        }
 
2111
 
 
2112
        songs_size = 0;
 
2113
 
 
2114
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
2115
 
 
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*/);
 
2120
                        } else {
 
2121
                                playlist_child_stats(&iter, &count, &duration, &songs_size, selected);
 
2122
                        }
 
2123
                } else {
 
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);
 
2127
                                duration += len;
 
2128
                                count++;
 
2129
                                if (stat(tstr, &statbuf) != -1) {
 
2130
                                        songs_size += statbuf.st_size / 1024.0;
 
2131
                                }
 
2132
                                g_free(tstr);
 
2133
                        }
 
2134
                }
 
2135
        }
 
2136
 
 
2137
 
 
2138
        time2time(duration, time);
 
2139
        m_size = songs_size / 1024.0;
 
2140
 
 
2141
        if (count == 1) {
 
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);
 
2145
                        } else {
 
2146
                                sprintf(str, _("%d track [%s] (%.1f GB)"), count, time, m_size / 1024.0);
 
2147
                        }
 
2148
                } else {
 
2149
                        sprintf(str, _("%d track [%s] "), count, time);
 
2150
                }
 
2151
        } else {
 
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);
 
2155
                        } else {
 
2156
                                sprintf(str, _("%d tracks [%s] (%.1f GB)"), count, time, m_size / 1024.0);
 
2157
                        }
 
2158
                } else {
 
2159
                        sprintf(str, _("%d tracks [%s] "), count, time);
 
2160
                }
 
2161
        }
 
2162
 
 
2163
        if (selected) {
 
2164
                gtk_label_set_text(GTK_LABEL(statusbar_selected), str);
 
2165
        } else {
 
2166
                gtk_label_set_text(GTK_LABEL(statusbar_total), str);
 
2167
        }
 
2168
}
 
2169
 
 
2170
 
 
2171
void
 
2172
recalc_album_node(GtkTreeIter * iter) {
 
2173
        gint count = 0;
 
2174
        gfloat duration = 0;
 
2175
        gfloat songs_size;
 
2176
        gchar time[MAXLEN];
 
2177
        gchar * color;
 
2178
 
 
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);
 
2182
 
 
2183
        gtk_tree_model_get(GTK_TREE_MODEL(play_store), iter, COLUMN_SELECTION_COLOR, &color, -1);
 
2184
        if (strcmp(color, pl_color_active) == 0) {
 
2185
                unmark_track(iter);
 
2186
                mark_track(iter);
 
2187
        }
 
2188
        g_free(color);
 
2189
}
 
2190
 
 
2191
 
 
2192
void
 
2193
playlist_selection_changed(GtkTreeSelection * sel, gpointer data) {
 
2194
 
 
2195
        playlist_stats(1/* true */);
 
2196
}
 
2197
 
 
2198
 
 
2199
void
 
2200
playlist_content_changed(void) {
 
2201
 
 
2202
        if (pl_progress_bar_semaphore == 0) {
 
2203
                playlist_stats(0/*false*/);
 
2204
        }
 
2205
}
 
2206
 
 
2207
 
 
2208
void
 
2209
playlist_drag_data_get(GtkWidget * widget, GdkDragContext * drag_context,
 
2210
                      GtkSelectionData * data, guint info, guint time, gpointer user_data) {
 
2211
 
 
2212
        gtk_selection_data_set(data, data->target, 8, (const guchar *) "list\0", 5);
 
2213
}
 
2214
 
 
2215
 
 
2216
void
 
2217
playlist_perform_drag(GtkTreeModel * model,
 
2218
                      GtkTreeIter * sel_iter, GtkTreePath * sel_path,
 
2219
                      GtkTreeIter * pos_iter, GtkTreePath * pos_path) {
 
2220
 
 
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);
 
2224
 
 
2225
        gint * sel_idx = gtk_tree_path_get_indices(sel_path);
 
2226
        gint * pos_idx = gtk_tree_path_get_indices(pos_path);
 
2227
 
 
2228
        if (cmp == 0) {
 
2229
                return;
 
2230
        }
 
2231
 
 
2232
        if (sel_depth == pos_depth && (sel_depth == 1 /* top */ || sel_idx[0] == pos_idx[0])) {
 
2233
 
 
2234
                GtkTreeIter parent;
 
2235
 
 
2236
                if (cmp == 1) {
 
2237
                        gtk_tree_store_move_before(play_store, sel_iter, pos_iter);
 
2238
                } else {
 
2239
                        gtk_tree_store_move_after(play_store, sel_iter, pos_iter);
 
2240
                }
 
2241
 
 
2242
                if (gtk_tree_model_iter_parent(model, &parent, sel_iter)) {
 
2243
                        unmark_track(&parent);
 
2244
                        mark_track(&parent);
 
2245
                }
 
2246
        } else {
 
2247
 
 
2248
                GtkTreeIter iter;
 
2249
                GtkTreeIter sel_parent;
 
2250
                GtkTreeIter pos_parent;
 
2251
                gint recalc_sel_parent = 0;
 
2252
                gint recalc_pos_parent = 0;
 
2253
                gchar * tname;
 
2254
                gchar * fname;
 
2255
                gchar * color;
 
2256
                gfloat voladj;
 
2257
                gchar * voldisp;
 
2258
                gfloat duration;
 
2259
                gchar * durdisp;
 
2260
                gint fontw;
 
2261
 
 
2262
                if (gtk_tree_model_iter_has_child(model, sel_iter)) {
 
2263
                        return;
 
2264
                }
 
2265
 
 
2266
                if (gtk_tree_model_iter_parent(model, &sel_parent, sel_iter)) {
 
2267
                        recalc_sel_parent = 1;
 
2268
                }
 
2269
 
 
2270
                if (gtk_tree_model_iter_parent(model, &pos_parent, pos_iter)) {
 
2271
                        recalc_pos_parent = 1;
 
2272
                }
 
2273
 
 
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);
 
2279
 
 
2280
                if (cmp == 1) {
 
2281
                        gtk_tree_store_insert_before(play_store, &iter, NULL, pos_iter);
 
2282
                } else {
 
2283
                        gtk_tree_store_insert_after(play_store, &iter, NULL, pos_iter);
 
2284
                }
 
2285
 
 
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);
 
2290
 
 
2291
                gtk_tree_store_remove(play_store, sel_iter);
 
2292
 
 
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);
 
2298
                        } else {
 
2299
                                gtk_tree_store_remove(play_store, &sel_parent);
 
2300
                        }
 
2301
                }
 
2302
 
 
2303
                if (recalc_pos_parent) {
 
2304
                        recalc_album_node(&pos_parent);
 
2305
                        unmark_track(&pos_parent);
 
2306
                        mark_track(&pos_parent);
 
2307
                }
 
2308
 
 
2309
                g_free(tname);
 
2310
                g_free(fname);
 
2311
                g_free(color);
 
2312
                g_free(voldisp);
 
2313
                g_free(durdisp);
 
2314
        }
 
2315
}
 
2316
 
 
2317
 
 
2318
void
 
2319
playlist_remove_scroll_tags() {
 
2320
 
 
2321
        if (playlist_scroll_up_tag > 0) {       
 
2322
                g_source_remove(playlist_scroll_up_tag);
 
2323
                playlist_scroll_up_tag = -1;
 
2324
        }
 
2325
        if (playlist_scroll_dn_tag > 0) {       
 
2326
                g_source_remove(playlist_scroll_dn_tag);
 
2327
                playlist_scroll_dn_tag = -1;
 
2328
        }
 
2329
}
 
2330
 
 
2331
gint
 
2332
playlist_drag_data_received(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, 
 
2333
                            GtkSelectionData  * data, guint info, guint time) {
 
2334
 
 
2335
        GtkTreeViewColumn * column;
 
2336
 
 
2337
        if (info == 0) { /* drag and drop inside Aqualung */
 
2338
 
 
2339
                if (!strcmp((gchar *)data->data, "store")) {
 
2340
 
 
2341
                        GtkTreePath * path = NULL;
 
2342
                        GtkTreeIter * piter = NULL;
 
2343
                        GtkTreeIter iter;
 
2344
                        GtkTreeIter parent;
 
2345
                        GtkTreeModel * model;
 
2346
                        int depth;
 
2347
 
 
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));
 
2350
#ifdef HAVE_CDDA
 
2351
                                if (is_store_iter_cdda(&iter))
 
2352
                                        depth += 1;
 
2353
#endif /* HAVE_CDDA */
 
2354
                        } else {
 
2355
                                return FALSE;
 
2356
                        }
 
2357
 
 
2358
                        if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(play_list),
 
2359
                                                          x, y, &path, &column, NULL, NULL)) {
 
2360
 
 
2361
                                if (depth != 4) { /* dragging store, artist or record */
 
2362
                                        while (gtk_tree_path_get_depth(path) > 1) {
 
2363
                                                gtk_tree_path_up(path);
 
2364
                                        }
 
2365
                                }
 
2366
 
 
2367
                                gtk_tree_model_get_iter(GTK_TREE_MODEL(play_store), &iter, path);
 
2368
                                piter = &iter;
 
2369
                        }
 
2370
 
 
2371
                        switch (depth) {
 
2372
                        case 1:
 
2373
                                store__addlist_defmode(piter);
 
2374
                                break;
 
2375
                        case 2:
 
2376
                                artist__addlist_defmode(piter);
 
2377
                                break;
 
2378
                        case 3:
 
2379
                                record__addlist_defmode(piter);
 
2380
                                break;
 
2381
                        case 4:
 
2382
                                track__addlist_cb(piter);
 
2383
 
 
2384
                                if (piter && gtk_tree_model_iter_parent(GTK_TREE_MODEL(play_store),
 
2385
                                                                        &parent, piter)) {
 
2386
                                        recalc_album_node(&parent);
 
2387
                                        unmark_track(&parent);
 
2388
                                        mark_track(&parent);
 
2389
                                }
 
2390
 
 
2391
                                break;
 
2392
                        }
 
2393
 
 
2394
                        if (path) {
 
2395
                                gtk_tree_path_free(path);
 
2396
                        }
 
2397
                        
 
2398
                } else if (!strcmp((gchar *)data->data, "list")) {
 
2399
 
 
2400
                        GtkTreeModel * model;
 
2401
                        GtkTreeIter sel_iter;
 
2402
                        GtkTreeIter pos_iter;
 
2403
                        GtkTreePath * sel_path = NULL;
 
2404
                        GtkTreePath * pos_path = NULL;
 
2405
 
 
2406
                        gtk_tree_selection_set_mode(play_select, GTK_SELECTION_SINGLE);
 
2407
 
 
2408
                        if (gtk_tree_selection_get_selected(play_select, &model, &sel_iter)) {
 
2409
 
 
2410
                                sel_path = gtk_tree_model_get_path(model, &sel_iter);
 
2411
 
 
2412
                                if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(play_list),
 
2413
                                                                  x, y, &pos_path, &column, NULL, NULL)) {
 
2414
 
 
2415
                                        gtk_tree_model_get_iter(model, &pos_iter, pos_path);
 
2416
 
 
2417
                                        playlist_perform_drag(model,
 
2418
                                                              &sel_iter, sel_path,
 
2419
                                                              &pos_iter, pos_path);
 
2420
                                }
 
2421
                        }
 
2422
 
 
2423
                        if (sel_path) {
 
2424
                                gtk_tree_path_free(sel_path);
 
2425
                        }
 
2426
 
 
2427
                        if (pos_path) {
 
2428
                                gtk_tree_path_free(pos_path);
 
2429
                        }
 
2430
 
 
2431
                        gtk_tree_selection_set_mode(play_select, GTK_SELECTION_MULTIPLE);
 
2432
                }
 
2433
 
 
2434
        } else { /* drag and drop from external app */
 
2435
 
 
2436
                gchar ** uri_list;
 
2437
                gchar * str = NULL;
 
2438
                int i;
 
2439
                char file[MAXLEN];
 
2440
                struct stat st_file;
 
2441
 
 
2442
                for (i = 0; *((gchar *)data->data + i); i++) {
 
2443
                        if (*((gchar *)data->data + i) == '\r') {
 
2444
                                *((gchar *)data->data + i) = '\n';
 
2445
                        }
 
2446
                }
 
2447
 
 
2448
                uri_list = g_strsplit((gchar *)data->data, "\n", 0);
 
2449
 
 
2450
                for (i = 0; uri_list[i]; i++) {
 
2451
 
 
2452
                        if (*(uri_list[i]) == '\0') {
 
2453
                                continue;
 
2454
                        }
 
2455
 
 
2456
                        if ((str = g_filename_from_uri(uri_list[i], NULL, NULL)) != NULL) {
 
2457
                                strncpy(file, str, MAXLEN-1);
 
2458
                                g_free(str);
 
2459
                        } else {
 
2460
                                int off = 0;
 
2461
 
 
2462
                                if (strstr(uri_list[i], "file:") == uri_list[i] ||
 
2463
                                    strstr(uri_list[i], "FILE:") == uri_list[i]) {
 
2464
                                        off = 5;
 
2465
                                }
 
2466
 
 
2467
                                while (uri_list[i][off] == '/' && uri_list[i][off+1] == '/') {
 
2468
                                        off++;
 
2469
                                }
 
2470
 
 
2471
                                strncpy(file, uri_list[i] + off, MAXLEN-1);
 
2472
                        }
 
2473
 
 
2474
                        if ((str = g_locale_from_utf8(file, -1, NULL, NULL, NULL)) != NULL) {
 
2475
                                strncpy(file, str, MAXLEN-1);
 
2476
                                g_free(str);
 
2477
                        }
 
2478
 
 
2479
                        if (stat(file, &st_file) == 0) {
 
2480
 
 
2481
                                GSList * list = g_slist_append(NULL, strdup(file));
 
2482
 
 
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)
 
2487
                                } else {
 
2488
                                        AQUALUNG_THREAD_CREATE(playlist_thread_id, NULL,
 
2489
                                                               add_files_to_playlist_thread, list)
 
2490
                                }
 
2491
                        }
 
2492
                }
 
2493
 
 
2494
                g_strfreev(uri_list);
 
2495
 
 
2496
                playlist_remove_scroll_tags();
 
2497
        }
 
2498
 
 
2499
        return FALSE;
 
2500
}
 
2501
 
 
2502
gint
 
2503
playlist_scroll_up(gpointer data) {
 
2504
 
 
2505
        g_signal_emit_by_name(G_OBJECT(scrolled_win), "scroll-child",
 
2506
                              GTK_SCROLL_STEP_BACKWARD, FALSE/*vertical*/, NULL);
 
2507
 
 
2508
        return TRUE;
 
2509
}
 
2510
 
 
2511
gint
 
2512
playlist_scroll_dn(gpointer data) {
 
2513
 
 
2514
        g_signal_emit_by_name(G_OBJECT(scrolled_win), "scroll-child",
 
2515
                              GTK_SCROLL_STEP_FORWARD, FALSE/*vertical*/, NULL);
 
2516
 
 
2517
        return TRUE;
 
2518
}
 
2519
 
 
2520
 
 
2521
void
 
2522
playlist_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time) {
 
2523
 
 
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);
 
2527
                }
 
2528
        } else {
 
2529
                if (playlist_scroll_dn_tag == -1) {
 
2530
                        playlist_scroll_dn_tag = g_timeout_add(100, playlist_scroll_dn, NULL);
 
2531
                }
 
2532
        }
 
2533
}
 
2534
 
 
2535
 
 
2536
gboolean
 
2537
playlist_drag_motion(GtkWidget * widget, GdkDragContext * context,
 
2538
                     gint x, gint y, guint time) {
 
2539
 
 
2540
        playlist_drag_y = y;
 
2541
        playlist_remove_scroll_tags();
 
2542
 
 
2543
        return TRUE;
 
2544
}
 
2545
 
 
2546
 
 
2547
void
 
2548
playlist_drag_end(GtkWidget * widget, GdkDragContext * drag_context, gpointer data) {
 
2549
 
 
2550
        playlist_remove_scroll_tags();
 
2551
}
 
2552
 
 
2553
 
 
2554
#ifdef HAVE_CDDA
 
2555
 
 
2556
void
 
2557
playlist_add_cdda(GtkTreeIter * iter_drive, unsigned long hash) {
 
2558
 
 
2559
        int i = 0;
 
2560
        GtkTreeIter iter;
 
2561
 
 
2562
        int target_found = 0;
 
2563
        GtkTreeIter target_iter;
 
2564
 
 
2565
 
 
2566
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
2567
 
 
2568
                if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) > 0) {
 
2569
 
 
2570
                        int j = 0;
 
2571
                        int has_cdda = 0;
 
2572
                        GtkTreeIter child;
 
2573
 
 
2574
                        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &child, &iter, j++)) {
 
2575
 
 
2576
                                char * pfile;
 
2577
                                gtk_tree_model_get(GTK_TREE_MODEL(play_store), &child,
 
2578
                                                   COLUMN_PHYSICAL_FILENAME, &pfile, -1);
 
2579
 
 
2580
                                if (!target_found && strstr(pfile, "CDDA") == pfile) {
 
2581
                                        has_cdda = 1;
 
2582
                                }
 
2583
 
 
2584
                                if (cdda_hash_matches(pfile, hash)) {
 
2585
                                        g_free(pfile);
 
2586
                                        return;
 
2587
                                }
 
2588
 
 
2589
                                g_free(pfile);
 
2590
                        }
 
2591
 
 
2592
                        if (!target_found && !has_cdda) {
 
2593
                                target_iter = iter;
 
2594
                                target_found = 1;
 
2595
                        }
 
2596
                } else {
 
2597
 
 
2598
                        char * pfile;
 
2599
                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
 
2600
                                           COLUMN_PHYSICAL_FILENAME, &pfile, -1);
 
2601
 
 
2602
                        if (!target_found && strstr(pfile, "CDDA") != pfile) {
 
2603
                                target_iter = iter;
 
2604
                                target_found = 1;
 
2605
                        }
 
2606
 
 
2607
                        if (cdda_hash_matches(pfile, hash)) {
 
2608
                                g_free(pfile);
 
2609
                                return;
 
2610
                        }
 
2611
                                
 
2612
                        g_free(pfile);
 
2613
                }
 
2614
        }
 
2615
 
 
2616
        if (target_found) {
 
2617
                record_addlist_iter(*iter_drive, &target_iter, options.playlist_is_tree);
 
2618
        } else {
 
2619
                record_addlist_iter(*iter_drive, NULL, options.playlist_is_tree);
 
2620
        }
 
2621
}
 
2622
 
 
2623
void
 
2624
playlist_remove_cdda(char * device_path) {
 
2625
 
 
2626
        int i = 0;
 
2627
        GtkTreeIter iter;
 
2628
        unsigned long hash;
 
2629
 
 
2630
        cdda_drive_t * drive = cdda_get_drive_by_device_path(device_path);
 
2631
 
 
2632
 
 
2633
        if (drive == NULL) {
 
2634
                return;
 
2635
        }
 
2636
 
 
2637
        if (drive->disc.hash == 0) {
 
2638
                hash = drive->disc.hash_prev;
 
2639
        } else {
 
2640
                hash = drive->disc.hash;
 
2641
        }
 
2642
 
 
2643
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
2644
 
 
2645
                if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) > 0) {
 
2646
 
 
2647
                        int j = 0;
 
2648
                        GtkTreeIter child;
 
2649
 
 
2650
                        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &child, &iter, j++)) {
 
2651
 
 
2652
                                char * pfile;
 
2653
                                gtk_tree_model_get(GTK_TREE_MODEL(play_store), &child,
 
2654
                                                   COLUMN_PHYSICAL_FILENAME, &pfile, -1);
 
2655
 
 
2656
                                if (cdda_hash_matches(pfile, hash)) {
 
2657
                                        gtk_tree_store_remove(play_store, &child);
 
2658
                                        --j;
 
2659
                                }
 
2660
                                
 
2661
                                g_free(pfile);
 
2662
                        }
 
2663
 
 
2664
                        if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter) == 0) {
 
2665
                                gtk_tree_store_remove(play_store, &iter);
 
2666
                                --i;
 
2667
                        } else {
 
2668
                                recalc_album_node(&iter);
 
2669
                        }
 
2670
 
 
2671
                } else {
 
2672
 
 
2673
                        char * pfile;
 
2674
                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter,
 
2675
                                           COLUMN_PHYSICAL_FILENAME, &pfile, -1);
 
2676
                        
 
2677
                        if (cdda_hash_matches(pfile, hash)) {
 
2678
                                gtk_tree_store_remove(play_store, &iter);
 
2679
                                --i;
 
2680
                        }
 
2681
 
 
2682
                        g_free(pfile);
 
2683
                }
 
2684
        }
 
2685
 
 
2686
        playlist_content_changed();
 
2687
}
 
2688
 
 
2689
#endif /* HAVE_CDDA */
 
2690
 
 
2691
 
 
2692
void
 
2693
create_playlist(void) {
 
2694
 
 
2695
        GtkWidget * vbox;
 
2696
 
 
2697
        GtkWidget * hbox_bottom;
 
2698
        GtkWidget * add_button;
 
2699
        GtkWidget * selall_button;
 
2700
        GtkWidget * remsel_button;
 
2701
 
 
2702
        GtkWidget * viewport;
 
2703
 
 
2704
        GtkWidget * statusbar;
 
2705
        GtkWidget * statusbar_scrolledwin;
 
2706
        GtkWidget * statusbar_viewport;
 
2707
 
 
2708
        gint i;
 
2709
 
 
2710
        GdkPixbuf * pixbuf;
 
2711
        gchar path[MAXLEN];
 
2712
 
 
2713
        GtkTargetEntry source_table[] = {
 
2714
                { "STRING", GTK_TARGET_SAME_APP, 0 }
 
2715
        };
 
2716
 
 
2717
        GtkTargetEntry target_table[] = {
 
2718
                { "STRING", GTK_TARGET_SAME_APP, 0 },
 
2719
                { "STRING", 0, 1 },
 
2720
                { "text/plain", 0, 1 },
 
2721
                { "text/uri-list", 0, 1 }
 
2722
        };
 
2723
 
 
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);
 
2732
        } else {
 
2733
                gtk_widget_set_size_request(playlist_window, 200, 200);
 
2734
        }
 
2735
 
 
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);
 
2739
        }
 
2740
 
 
2741
        gtk_widget_set_events(playlist_window, GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
 
2742
 
 
2743
 
 
2744
        if (!options.playlist_is_embedded) {
 
2745
                plist_menu = gtk_menu_new();
 
2746
                init_plist_menu(plist_menu);
 
2747
        }
 
2748
 
 
2749
        vbox = gtk_vbox_new(FALSE, 2);
 
2750
        gtk_container_add(GTK_CONTAINER(playlist_window), vbox);
 
2751
 
 
2752
        /* create playlist */
 
2753
        if (!play_store) {
 
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 */
 
2763
        }
 
2764
 
 
2765
 
 
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);
 
2770
 
 
2771
        if (options.override_skin_settings) {
 
2772
                gtk_widget_modify_font (play_list, fd_playlist);
 
2773
        }
 
2774
 
 
2775
        if (options.enable_pl_rules_hint) {
 
2776
                gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(play_list), TRUE);
 
2777
        }
 
2778
 
 
2779
        playlist_color_is_set = 0;
 
2780
 
 
2781
        if (pl_color_active[0] == '\0') {
 
2782
                strcpy(pl_color_active, "#ffffff");
 
2783
        }
 
2784
        if (pl_color_inactive[0] == '\0') {
 
2785
                strcpy(pl_color_inactive, "#010101");
 
2786
        }
 
2787
 
 
2788
        for (i = 0; i < 3; i++) {
 
2789
                switch (options.plcol_idx[i]) {
 
2790
                case 0:
 
2791
                        track_renderer = gtk_cell_renderer_text_new();
 
2792
                        track_column = gtk_tree_view_column_new_with_attributes("Tracks",
 
2793
                                                                                track_renderer,
 
2794
                                                                                "text", 0,
 
2795
                                                                                "foreground", 2,
 
2796
                                                                                "weight", 7,
 
2797
                                                                                NULL);
 
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);
 
2806
                        break;
 
2807
 
 
2808
                case 1:
 
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",
 
2812
                                                                              rva_renderer,
 
2813
                                                                              "text", 4,
 
2814
                                                                              "foreground", 2,
 
2815
                                                                              "weight", 7,
 
2816
                                                                              NULL);
 
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);
 
2823
                        } else {
 
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);
 
2826
                        }
 
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);
 
2829
                        break;
 
2830
 
 
2831
                case 2:
 
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",
 
2835
                                                                                 length_renderer,
 
2836
                                                                                 "text", 6,
 
2837
                                                                                 "foreground", 2,
 
2838
                                                                                 "weight", 7,
 
2839
                                                                                 NULL);
 
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);
 
2846
                        } else {
 
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);
 
2849
                        }
 
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);
 
2852
                }
 
2853
        }
 
2854
 
 
2855
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(play_list), FALSE);
 
2856
 
 
2857
 
 
2858
        /* setup drag and drop */
 
2859
        
 
2860
        gtk_drag_source_set(play_list,
 
2861
                            GDK_BUTTON1_MASK,
 
2862
                            source_table,
 
2863
                            1,
 
2864
                            GDK_ACTION_MOVE);
 
2865
        
 
2866
        gtk_drag_dest_set(play_list,
 
2867
                          GTK_DEST_DEFAULT_ALL,
 
2868
                          target_table,
 
2869
                          4,
 
2870
                          GDK_ACTION_MOVE | GDK_ACTION_COPY);
 
2871
 
 
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);
 
2875
        }       
 
2876
 
 
2877
        g_signal_connect(G_OBJECT(play_list), "drag_end",
 
2878
                         G_CALLBACK(playlist_drag_end), NULL);
 
2879
 
 
2880
        g_signal_connect(G_OBJECT(play_list), "drag_data_get",
 
2881
                         G_CALLBACK(playlist_drag_data_get), NULL);
 
2882
 
 
2883
        g_signal_connect(G_OBJECT(play_list), "drag_leave",
 
2884
                         G_CALLBACK(playlist_drag_leave), NULL);
 
2885
 
 
2886
        g_signal_connect(G_OBJECT(play_list), "drag_motion",
 
2887
                         G_CALLBACK(playlist_drag_motion), NULL);
 
2888
 
 
2889
        g_signal_connect(G_OBJECT(play_list), "drag_data_received",
 
2890
                         G_CALLBACK(playlist_drag_data_received), NULL);
 
2891
        
 
2892
 
 
2893
        play_select = gtk_tree_view_get_selection(GTK_TREE_VIEW(play_list));
 
2894
        gtk_tree_selection_set_mode(play_select, GTK_SELECTION_MULTIPLE);
 
2895
 
 
2896
        g_signal_connect(G_OBJECT(play_select), "changed",
 
2897
                         G_CALLBACK(playlist_selection_changed), NULL);
 
2898
 
 
2899
 
 
2900
        g_signal_connect(G_OBJECT(play_list), "button_press_event",
 
2901
                         G_CALLBACK(doubleclick_handler), NULL);
 
2902
 
 
2903
 
 
2904
        viewport = gtk_viewport_new(NULL, NULL);
 
2905
        gtk_box_pack_start(GTK_BOX(vbox), viewport, TRUE, TRUE, 0);
 
2906
 
 
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);
 
2911
 
 
2912
        gtk_container_add(GTK_CONTAINER(scrolled_win), play_list);
 
2913
 
 
2914
 
 
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);
 
2917
 
 
2918
        if (pl_progress_bar_semaphore > 0) {
 
2919
                playlist_progress_bar_show();
 
2920
        }
 
2921
 
 
2922
        /* statusbar */
 
2923
        if (options.enable_playlist_statusbar) {
 
2924
 
 
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);
 
2930
 
 
2931
                statusbar_viewport = gtk_viewport_new(NULL, NULL);
 
2932
                gtk_widget_set_name(statusbar_viewport, "info_viewport");
 
2933
 
 
2934
                gtk_container_add(GTK_CONTAINER(statusbar_scrolledwin), statusbar_viewport);
 
2935
                gtk_box_pack_start(GTK_BOX(vbox), statusbar_scrolledwin, FALSE, TRUE, 2);
 
2936
 
 
2937
                gtk_widget_set_events(statusbar_viewport,
 
2938
                                      GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
 
2939
 
 
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);
 
2946
                
 
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);
 
2950
                
 
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);
 
2954
                
 
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);
 
2958
                
 
2959
                gtk_box_pack_end(GTK_BOX(statusbar), gtk_vseparator_new(), FALSE, TRUE, 5);
 
2960
                
 
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);
 
2964
                
 
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);
 
2968
 
 
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);
 
2974
                }
 
2975
                
 
2976
                playlist_selection_changed(NULL, NULL);
 
2977
                playlist_content_changed();
 
2978
        }
 
2979
        
 
2980
 
 
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);
 
2984
 
 
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);
 
2990
 
 
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);
 
2996
        
 
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);
 
3002
 
 
3003
 
 
3004
        add_menu = gtk_menu_new();
 
3005
 
 
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);
 
3010
 
 
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);
 
3015
 
 
3016
        g_signal_connect_swapped(G_OBJECT(add_button), "event", G_CALLBACK(add_cb), NULL);
 
3017
 
 
3018
 
 
3019
        sel_menu = gtk_menu_new();
 
3020
 
 
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);
 
3025
 
 
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);
 
3030
 
 
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);
 
3035
 
 
3036
        g_signal_connect_swapped(G_OBJECT(selall_button), "event", G_CALLBACK(sel_cb), NULL);
 
3037
 
 
3038
 
 
3039
        rem_menu = gtk_menu_new();
 
3040
 
 
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);
 
3045
 
 
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);
 
3050
 
 
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);
 
3055
 
 
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);
 
3060
 
 
3061
        g_signal_connect_swapped(G_OBJECT(remsel_button), "event", G_CALLBACK(rem_cb), NULL);
 
3062
 
 
3063
 
 
3064
        g_signal_connect(G_OBJECT(playlist_window), "size_allocate",
 
3065
                         G_CALLBACK(playlist_size_allocate), NULL);
 
3066
 
 
3067
}        
 
3068
        
 
3069
 
 
3070
 
 
3071
void
 
3072
show_playlist(void) {
 
3073
 
 
3074
        options.playlist_on = 1;
 
3075
 
 
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);
 
3079
        } else {
 
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);
 
3082
        }
 
3083
 
 
3084
        gtk_widget_show_all(playlist_window);
 
3085
 
 
3086
        if (!playlist_color_is_set) {
 
3087
                set_playlist_color();
 
3088
                playlist_color_is_set = 1;
 
3089
        }
 
3090
}
 
3091
 
 
3092
 
 
3093
void
 
3094
hide_playlist(void) {
 
3095
 
 
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);
 
3100
        } else {
 
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);
 
3104
        }
 
3105
        gtk_widget_hide(playlist_window);
 
3106
 
 
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);
 
3109
        }
 
3110
 
 
3111
}
 
3112
 
 
3113
 
 
3114
xmlNodePtr
 
3115
save_track_node(GtkTreeIter * piter, xmlNodePtr root, char * nodeID) {
 
3116
 
 
3117
        gchar * track_name;
 
3118
        gchar * phys_name;
 
3119
        gchar *converted_temp;
 
3120
        gchar * color;
 
3121
        gfloat voladj;
 
3122
        gfloat duration;
 
3123
        gchar str[32];
 
3124
        xmlNodePtr node;
 
3125
 
 
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);
 
3130
        
 
3131
        node = xmlNewTextChild(root, NULL, (const xmlChar*) nodeID, NULL);
 
3132
        
 
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);
 
3136
        } else {
 
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);
 
3143
                } else {
 
3144
                        converted_temp = g_filename_to_uri(phys_name, NULL, NULL);
 
3145
                }
 
3146
                xmlNewTextChild(node, NULL, (const xmlChar*) "phys_name", (const xmlChar*) converted_temp);
 
3147
                g_free(converted_temp);
 
3148
        };
 
3149
 
 
3150
        /* FIXME: don't use #000000 (black) color for active song */
 
3151
        
 
3152
        if (strcmp(color, pl_color_active) == 0) {
 
3153
                strcpy(str, "yes");
 
3154
        } else {
 
3155
                strcpy(str, "no");
 
3156
        }
 
3157
        xmlNewTextChild(node, NULL, (const xmlChar*) "is_active", (const xmlChar*) str);
 
3158
        
 
3159
        snprintf(str, 31, "%f", voladj);
 
3160
        xmlNewTextChild(node, NULL, (const xmlChar*) "voladj", (const xmlChar*) str);
 
3161
        
 
3162
        snprintf(str, 31, "%f", duration);
 
3163
        xmlNewTextChild(node, NULL, (const xmlChar*) "duration", (const xmlChar*) str);
 
3164
        
 
3165
        g_free(track_name);
 
3166
        g_free(phys_name);
 
3167
        g_free(color);
 
3168
 
 
3169
        return node;
 
3170
}
 
3171
 
 
3172
 
 
3173
void
 
3174
save_playlist(char * filename) {
 
3175
 
 
3176
        gint i = 0;
 
3177
        GtkTreeIter iter;
 
3178
        xmlDocPtr doc;
 
3179
        xmlNodePtr root;
 
3180
 
 
3181
        doc = xmlNewDoc((const xmlChar*) "1.0");
 
3182
        root = xmlNewNode(NULL, (const xmlChar*) "aqualung_playlist");
 
3183
        xmlDocSetRootElement(doc, root);
 
3184
 
 
3185
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
3186
 
 
3187
                gint n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter);
 
3188
 
 
3189
                if (n) { /* album node */
 
3190
                        gint j = 0;
 
3191
                        GtkTreeIter iter_child;
 
3192
                        xmlNodePtr node;
 
3193
 
 
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");
 
3198
                        }
 
3199
                } else { /* track node */
 
3200
                        save_track_node(&iter, root, "track");
 
3201
                }
 
3202
        }
 
3203
        xmlSaveFormatFile(filename, doc, 1);
 
3204
        xmlFreeDoc(doc);
 
3205
}
 
3206
 
 
3207
 
 
3208
void
 
3209
parse_playlist_track(xmlDocPtr doc, xmlNodePtr _cur, int level) {
 
3210
 
 
3211
        xmlChar * key;
 
3212
        gchar *converted_temp;
 
3213
        gint is_record_node;
 
3214
 
 
3215
        xmlNodePtr cur;
 
3216
        playlist_filemeta * plfm = NULL;
 
3217
 
 
3218
        
 
3219
        is_record_node = !xmlStrcmp(_cur->name, (const xmlChar *)"record");
 
3220
 
 
3221
        if ((plfm = (playlist_filemeta *)calloc(1, sizeof(playlist_filemeta))) == NULL) {
 
3222
                fprintf(stderr, "calloc error in parse_playlist_track()\n");
 
3223
                return;
 
3224
        }
 
3225
 
 
3226
        plfm->level = level;
 
3227
 
 
3228
        for (cur = _cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
 
3229
 
 
3230
                if (!xmlStrcmp(cur->name, (const xmlChar *)"track_name")) {
 
3231
                        key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 
3232
                        if (key != NULL) {
 
3233
                                plfm->title = strndup((char *)key, MAXLEN-1);
 
3234
                        }
 
3235
                        xmlFree(key);
 
3236
                } else if (!xmlStrcmp(cur->name, (const xmlChar *)"phys_name")) {
 
3237
                        key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 
3238
                        if (key != NULL) {
 
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);
 
3245
                                } else {
 
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);
 
3251
                                        } else {
 
3252
                                                /* last try - maybe it's plain locale filename */
 
3253
                                                plfm->filename = strndup((char *)key, MAXLEN-1);
 
3254
                                        }
 
3255
                                }
 
3256
                        }
 
3257
                        xmlFree(key);
 
3258
                } else if (!xmlStrcmp(cur->name, (const xmlChar *)"is_active")) {
 
3259
                        key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 
3260
                        if (key != NULL) {
 
3261
                                if (!xmlStrcmp(key, (const xmlChar *)"yes")) {
 
3262
                                        plfm->active = 1;
 
3263
                                } else {
 
3264
                                        plfm->active = 0;
 
3265
                                }
 
3266
                        } else {
 
3267
                                plfm->active = 0;
 
3268
                        }
 
3269
                        xmlFree(key);
 
3270
                } else if (!xmlStrcmp(cur->name, (const xmlChar *)"voladj")) {
 
3271
                        key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 
3272
                        if (key != NULL) {
 
3273
                                plfm->voladj = convf((char *)key);
 
3274
                        }
 
3275
                        xmlFree(key);
 
3276
                } else if (!xmlStrcmp(cur->name, (const xmlChar *)"duration")) {
 
3277
                        key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 
3278
                        if (key != NULL) {
 
3279
                                plfm->duration = convf((char *)key);
 
3280
                        }
 
3281
                        xmlFree(key);
 
3282
                }
 
3283
        }
 
3284
 
 
3285
        playlist_thread_add_to_list(plfm);
 
3286
 
 
3287
        if (is_record_node) {
 
3288
 
 
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);
 
3292
                        }
 
3293
                }
 
3294
        }
 
3295
}
 
3296
 
 
3297
void
 
3298
load_playlist(char * filename, int enqueue) {
 
3299
 
 
3300
        xmlDocPtr doc;
 
3301
        xmlNodePtr cur;
 
3302
        FILE * f;
 
3303
 
 
3304
        if ((f = fopen(filename, "rt")) == NULL) {
 
3305
                return;
 
3306
        }
 
3307
        fclose(f);
 
3308
 
 
3309
        doc = xmlParseFile(filename);
 
3310
        if (doc == NULL) {
 
3311
                fprintf(stderr, "An XML error occured while parsing %s\n", filename);
 
3312
                return;
 
3313
        }
 
3314
 
 
3315
        cur = xmlDocGetRootElement(doc);
 
3316
        if (cur == NULL) {
 
3317
                fprintf(stderr, "load_playlist: empty XML document\n");
 
3318
                xmlFreeDoc(doc);
 
3319
                return;
 
3320
        }
 
3321
 
 
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");
 
3325
                xmlFreeDoc(doc);
 
3326
                return;
 
3327
        }
 
3328
 
 
3329
        if (!enqueue) {
 
3330
                playlist_thread_add_to_list(NULL);
 
3331
        }
 
3332
 
 
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);
 
3337
                }
 
3338
        }
 
3339
 
 
3340
        xmlFreeDoc(doc);
 
3341
}
 
3342
 
 
3343
 
 
3344
void
 
3345
load_m3u(char * filename, int enqueue) {
 
3346
 
 
3347
        FILE * f;
 
3348
        gint c;
 
3349
        gint i = 0;
 
3350
        gint n;
 
3351
        gchar * str;
 
3352
        gchar pl_dir[MAXLEN];
 
3353
        gchar line[MAXLEN];
 
3354
        gchar name[MAXLEN];
 
3355
        gchar path[MAXLEN];
 
3356
        gchar tmp[MAXLEN];
 
3357
        gint have_name = 0;
 
3358
        playlist_filemeta * plfm = NULL;
 
3359
 
 
3360
 
 
3361
        if ((str = strrchr(filename, '/')) == NULL) {
 
3362
                printf("load_m3u(): programmer error: playlist path is not absolute\n");
 
3363
        }
 
3364
        for (i = 0; (i < (str - filename)) && (i < MAXLEN-1); i++) {
 
3365
                pl_dir[i] = filename[i];
 
3366
        }
 
3367
        pl_dir[i] = '\0';
 
3368
 
 
3369
        if ((f = fopen(filename, "rb")) == NULL) {
 
3370
                fprintf(stderr, "unable to open .m3u playlist: %s\n", filename);
 
3371
                return;
 
3372
        }
 
3373
 
 
3374
        if (!enqueue) {
 
3375
                playlist_thread_add_to_list(NULL);
 
3376
        }
 
3377
 
 
3378
        i = 0;
 
3379
        while ((c = fgetc(f)) != EOF && !playlist_thread_stop) {
 
3380
                if ((c != '\n') && (c != '\r') && (i < MAXLEN)) {
 
3381
                        if ((i > 0) || ((c != ' ') && (c != '\t'))) {
 
3382
                                line[i++] = c;
 
3383
                        }
 
3384
                } else {
 
3385
                        line[i] = '\0';
 
3386
                        if (i == 0) {
 
3387
                                continue;
 
3388
                        }
 
3389
                        i = 0;
 
3390
 
 
3391
                        if (strstr(line, "#EXTM3U") == line) {
 
3392
                                continue;
 
3393
                        }
 
3394
                        
 
3395
                        if (strstr(line, "#EXTINF:") == line) {
 
3396
 
 
3397
                                char str_duration[64];
 
3398
                                int cnt = 0;
 
3399
                                
 
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];
 
3404
                                        ++cnt;
 
3405
                                }
 
3406
                                str_duration[cnt] = '\0';
 
3407
                                snprintf(name, MAXLEN-1, "%s", line+cnt+9);
 
3408
                                have_name = 1;
 
3409
 
 
3410
                        } else {
 
3411
                                /* safeguard against http:// and C:\ stuff */
 
3412
                                if (strstr(line, "http://") == line) {
 
3413
                                        fprintf(stderr, "Ignoring playlist item: %s\n", line);
 
3414
                                        i = 0;
 
3415
                                        have_name = 0;
 
3416
                                        continue;
 
3417
                                }
 
3418
                                if ((line[1] == ':') && (line[2] == '\\')) {
 
3419
                                        fprintf(stderr, "Ignoring playlist item: %s\n", line);
 
3420
                                        i = 0;
 
3421
                                        have_name = 0;
 
3422
                                        continue;
 
3423
                                }
 
3424
 
 
3425
                                snprintf(path, MAXLEN-1, "%s", line);
 
3426
 
 
3427
                                /* path curing: turn \-s into /-s */
 
3428
                                for (n = 0; n < strlen(path); n++) {
 
3429
                                        if (path[n] == '\\')
 
3430
                                                path[n] = '/';
 
3431
                                }
 
3432
                                
 
3433
                                if (path[0] != '/') {
 
3434
                                        strncpy(tmp, path, MAXLEN-1);
 
3435
                                        snprintf(path, MAXLEN-1, "%s/%s", pl_dir, tmp);
 
3436
                                }
 
3437
 
 
3438
                                if (!have_name) {
 
3439
                                        gchar * ch;
 
3440
                                        if ((ch = strrchr(path, '/')) != NULL) {
 
3441
                                                ++ch;
 
3442
                                                snprintf(name, MAXLEN-1, "%s", ch);
 
3443
                                        } else {
 
3444
                                                fprintf(stderr,
 
3445
                                                        "warning: ain't this a directory? : %s\n", path);
 
3446
                                                snprintf(name, MAXLEN-1, "%s", path);
 
3447
                                        }
 
3448
                                }
 
3449
                                have_name = 0;
 
3450
 
 
3451
                                
 
3452
                                plfm = playlist_filemeta_get(path,
 
3453
                                                             have_name ? name : NULL,
 
3454
                                                             1);
 
3455
                                if (plfm == NULL) {
 
3456
                                        fprintf(stderr, "load_m3u(): playlist_filemeta_get() returned NULL\n");
 
3457
                                } else {
 
3458
                                        playlist_thread_add_to_list(plfm);
 
3459
                                }
 
3460
                        }
 
3461
                }
 
3462
        }
 
3463
}
 
3464
 
 
3465
 
 
3466
void
 
3467
load_pls(char * filename, int enqueue) {
 
3468
 
 
3469
        FILE * f;
 
3470
        gint c;
 
3471
        gint i = 0;
 
3472
        gint n;
 
3473
        gchar * str;
 
3474
        gchar pl_dir[MAXLEN];
 
3475
        gchar line[MAXLEN];
 
3476
        gchar file[MAXLEN];
 
3477
        gchar title[MAXLEN];
 
3478
        gchar tmp[MAXLEN];
 
3479
        gint have_file = 0;
 
3480
        gint have_title = 0;
 
3481
        gchar numstr_file[10];
 
3482
        gchar numstr_title[10];
 
3483
        playlist_filemeta * plfm = NULL;
 
3484
 
 
3485
 
 
3486
        if ((str = strrchr(filename, '/')) == NULL) {
 
3487
                printf("load_pls(): programmer error: playlist path is not absolute\n");
 
3488
        }
 
3489
        for (i = 0; (i < (str - filename)) && (i < MAXLEN-1); i++) {
 
3490
                pl_dir[i] = filename[i];
 
3491
        }
 
3492
        pl_dir[i] = '\0';
 
3493
 
 
3494
        if ((f = fopen(filename, "rb")) == NULL) {
 
3495
                fprintf(stderr, "unable to open .pls playlist: %s\n", filename);
 
3496
                return;
 
3497
        }
 
3498
 
 
3499
        if (!enqueue) {
 
3500
                playlist_thread_add_to_list(NULL);
 
3501
        }
 
3502
 
 
3503
        i = 0;
 
3504
        while ((c = fgetc(f)) != EOF && !playlist_thread_stop) {
 
3505
                if ((c != '\n') && (c != '\r') && (i < MAXLEN)) {
 
3506
                        if ((i > 0) || ((c != ' ') && (c != '\t'))) {
 
3507
                                line[i++] = c;
 
3508
                        }
 
3509
                } else {
 
3510
                        line[i] = '\0';
 
3511
                        if (i == 0)
 
3512
                                continue;
 
3513
                        i = 0;
 
3514
 
 
3515
                        if (strstr(line, "[playlist]") == line) {
 
3516
                                continue;
 
3517
                        }
 
3518
                        if (strstr(line, "NumberOfEntries") == line) {
 
3519
                                continue;
 
3520
                        }
 
3521
                        if (strstr(line, "Version") == line) {
 
3522
                                continue;
 
3523
                        }
 
3524
                        
 
3525
                        if (strstr(line, "File") == line) {
 
3526
 
 
3527
                                char numstr[10];
 
3528
                                char * ch;
 
3529
                                int m;
 
3530
 
 
3531
                                if ((ch = strstr(line, "=")) == NULL) {
 
3532
                                        fprintf(stderr, "Syntax error in %s\n", filename);
 
3533
                                        return;
 
3534
                                }
 
3535
                                
 
3536
                                ++ch;
 
3537
                                while ((*ch == ' ') || (*ch == '\t'))
 
3538
                                        ++ch;
 
3539
 
 
3540
                                m = 0;
 
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];
 
3544
                                }
 
3545
                                numstr[m] = '\0';
 
3546
                                strncpy(numstr_file, numstr, sizeof(numstr_file));
 
3547
 
 
3548
                                /* safeguard against http:// and C:\ stuff */
 
3549
                                if (strstr(ch, "http://") == ch) {
 
3550
                                        fprintf(stderr, "Ignoring playlist item: %s\n", ch);
 
3551
                                        i = 0;
 
3552
                                        have_file = have_title = 0;
 
3553
                                        continue;
 
3554
                                }
 
3555
                                if ((ch[1] == ':') && (ch[2] == '\\')) {
 
3556
                                        fprintf(stderr, "Ignoring playlist item: %s\n", ch);
 
3557
                                        i = 0;
 
3558
                                        have_file = have_title = 0;
 
3559
                                        continue;
 
3560
                                }
 
3561
 
 
3562
                                snprintf(file, MAXLEN-1, "%s", ch);
 
3563
 
 
3564
                                /* path curing: turn \-s into /-s */
 
3565
 
 
3566
                                for (n = 0; n < strlen(file); n++) {
 
3567
                                        if (file[n] == '\\')
 
3568
                                                file[n] = '/';
 
3569
                                }
 
3570
 
 
3571
                                if (file[0] != '/') {
 
3572
                                        strncpy(tmp, file, MAXLEN-1);
 
3573
                                        snprintf(file, MAXLEN-1, "%s/%s", pl_dir, tmp);
 
3574
                                }
 
3575
 
 
3576
                                have_file = 1;
 
3577
                                
 
3578
 
 
3579
                        } else if (strstr(line, "Title") == line) {
 
3580
 
 
3581
                                char numstr[10];
 
3582
                                char * ch;
 
3583
                                int m;
 
3584
 
 
3585
                                if ((ch = strstr(line, "=")) == NULL) {
 
3586
                                        fprintf(stderr, "Syntax error in %s\n", filename);
 
3587
                                        return;
 
3588
                                }
 
3589
                                
 
3590
                                ++ch;
 
3591
                                while ((*ch == ' ') || (*ch == '\t'))
 
3592
                                        ++ch;
 
3593
 
 
3594
                                m = 0;
 
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];
 
3598
                                }
 
3599
                                numstr[m] = '\0';
 
3600
                                strncpy(numstr_title, numstr, sizeof(numstr_title));
 
3601
 
 
3602
                                snprintf(title, MAXLEN-1, "%s", ch);
 
3603
                                have_title = 1;
 
3604
 
 
3605
 
 
3606
                        } else if (strstr(line, "Length") == line) {
 
3607
 
 
3608
                                /* We parse the timing, but throw it away. 
 
3609
                                   This may change in the future. */
 
3610
 
 
3611
                                char * ch;
 
3612
                                if ((ch = strstr(line, "=")) == NULL) {
 
3613
                                        fprintf(stderr, "Syntax error in %s\n", filename);
 
3614
                                        return;
 
3615
                                }
 
3616
                                
 
3617
                                ++ch;
 
3618
                                while ((*ch == ' ') || (*ch == '\t'))
 
3619
                                        ++ch;
 
3620
 
 
3621
                        } else {
 
3622
                                fprintf(stderr, 
 
3623
                                        "Syntax error: invalid line in playlist: %s\n", line);
 
3624
                                return;
 
3625
                        }
 
3626
 
 
3627
                        if (!have_file || !have_title) {
 
3628
                                continue;
 
3629
                        }
 
3630
                        
 
3631
                        if (strcmp(numstr_file, numstr_title) != 0) {
 
3632
                                continue;
 
3633
                        }
 
3634
 
 
3635
                        have_file = have_title = 0;
 
3636
 
 
3637
                        plfm = playlist_filemeta_get(file,
 
3638
                                                     have_title ? title : NULL,
 
3639
                                                     1);
 
3640
                        if (plfm == NULL) {
 
3641
                                fprintf(stderr, "load_pls(): playlist_filemeta_get() returned NULL\n");
 
3642
                        } else {
 
3643
                                playlist_thread_add_to_list(plfm);
 
3644
                        }
 
3645
                }
 
3646
        }
 
3647
}
 
3648
 
 
3649
 
 
3650
/* return values: 
 
3651
 *   0: not a playlist 
 
3652
 *   1: native aqualung (XML)
 
3653
 *   2: .m3u
 
3654
 *   3: .pls
 
3655
 */
 
3656
int
 
3657
is_playlist(char * filename) {
 
3658
 
 
3659
        FILE * f;
 
3660
        gchar buf[] = "<?xml version=\"1.0\"?>\n<aqualung_playlist";
 
3661
        gchar inbuf[64];
 
3662
        gint len;
 
3663
 
 
3664
        if ((f = fopen(filename, "rb")) == NULL) {
 
3665
                return 0;
 
3666
        }
 
3667
        if (fread(inbuf, 1, strlen(buf)+1, f) != strlen(buf)+1) {
 
3668
                fclose(f);
 
3669
                goto _readext;
 
3670
        }
 
3671
        fclose(f);
 
3672
        inbuf[strlen(buf)] = '\0';
 
3673
 
 
3674
        if (strcmp(buf, inbuf) == 0) {
 
3675
                return 1;
 
3676
        }
 
3677
 
 
3678
 _readext:
 
3679
        len = strlen(filename);
 
3680
        if (len < 5) {
 
3681
                return 0;
 
3682
        }
 
3683
 
 
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'))) {
 
3688
                return 2;
 
3689
        }
 
3690
 
 
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'))) {
 
3695
                return 3;
 
3696
        }
 
3697
 
 
3698
        return 0;
 
3699
}
 
3700
 
 
3701
 
 
3702
void
 
3703
init_plist_menu(GtkWidget *append_menu) {
 
3704
 
 
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();
 
3709
 
 
3710
#ifdef HAVE_IFP
 
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 */
 
3714
 
 
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..."));
 
3723
 
 
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);
 
3728
 
 
3729
#ifdef HAVE_IFP
 
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 */
 
3733
 
 
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);
 
3742
 
 
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);
 
3749
 
 
3750
#ifdef HAVE_IFP
 
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 */
 
3753
 
 
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);
 
3756
 
 
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);
 
3766
 
 
3767
#ifdef HAVE_IFP
 
3768
        gtk_widget_show(plist__send_songs_to_iriver);
 
3769
        gtk_widget_show(plist__separator3);
 
3770
#endif  /* HAVE_IFP */
 
3771
        
 
3772
        gtk_widget_show(plist__fileinfo);
 
3773
        gtk_widget_show(plist__search);
 
3774
}
 
3775
 
 
3776
 
 
3777
void
 
3778
show_active_position_in_playlist(void) {
 
3779
 
 
3780
        GtkTreeIter iter;
 
3781
        gchar * str;
 
3782
        gint flag;
 
3783
        GtkTreePath * visible_path;
 
3784
        GtkTreeViewColumn * visible_column;
 
3785
 
 
3786
        flag = 0;
 
3787
 
 
3788
        if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(play_store), &iter)) {
 
3789
 
 
3790
                do {
 
3791
                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_SELECTION_COLOR, &str, -1);
 
3792
                        if (strcmp(str, pl_color_active) == 0) {
 
3793
                                flag = 1;
 
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);
 
3796
                                }
 
3797
                                break;
 
3798
                        }
 
3799
                        g_free(str);
 
3800
 
 
3801
                } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(play_store), &iter));
 
3802
 
 
3803
                if (!flag) {
 
3804
                        gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, 0);
 
3805
                }
 
3806
 
 
3807
                set_cursor_in_playlist(&iter, TRUE);
 
3808
        }
 
3809
}
 
3810
 
 
3811
void
 
3812
show_active_position_in_playlist_toggle(void) {
 
3813
 
 
3814
        GtkTreeIter iter, iter_child;
 
3815
        gchar * str;
 
3816
        gint flag, cflag, j;
 
3817
        GtkTreePath * visible_path;
 
3818
        GtkTreeViewColumn * visible_column;
 
3819
 
 
3820
        flag = cflag = 0;
 
3821
 
 
3822
        if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(play_store), &iter)) {
 
3823
 
 
3824
                do {
 
3825
                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_SELECTION_COLOR, &str, -1);
 
3826
                        if (strcmp(str, pl_color_active) == 0) {
 
3827
                                flag = 1;
 
3828
                                if (gtk_tree_selection_iter_is_selected(play_select, &iter) &&
 
3829
                                    gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter)) {
 
3830
 
 
3831
                                        gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &visible_path, &visible_column);
 
3832
 
 
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);
 
3835
                                        }
 
3836
 
 
3837
                                        j = 0;
 
3838
 
 
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) {
 
3842
                                                        cflag = 1;
 
3843
                                                        break;
 
3844
                                                }
 
3845
                                        }
 
3846
                                }
 
3847
                                break;
 
3848
                        }
 
3849
 
 
3850
                } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(play_store), &iter));
 
3851
 
 
3852
                if (flag) {
 
3853
                        if (cflag) {
 
3854
                                set_cursor_in_playlist(&iter_child, TRUE);
 
3855
                        } else {
 
3856
                                set_cursor_in_playlist(&iter, TRUE);
 
3857
                        }
 
3858
                }
 
3859
        }
 
3860
}
 
3861
 
 
3862
void
 
3863
show_last_position_in_playlist(void) {
 
3864
 
 
3865
        GtkTreeIter iter;
 
3866
        gint i;
 
3867
        GtkTreePath * visible_path;
 
3868
 
 
3869
        for (i = 0; gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i); i++);
 
3870
        
 
3871
        if (i) {
 
3872
                gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i-1);
 
3873
 
 
3874
                visible_path = gtk_tree_model_get_path (GTK_TREE_MODEL(play_store), &iter);
 
3875
                if (visible_path) {
 
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);
 
3880
                }
 
3881
        }
 
3882
}
 
3883
 
 
3884
void
 
3885
expand_collapse_album_node(void) {
 
3886
 
 
3887
        GtkTreeIter iter, iter_child;
 
3888
        gint i, j;
 
3889
        GtkTreePath *path;
 
3890
        GtkTreeViewColumn *column;
 
3891
 
 
3892
        i = 0;
 
3893
 
 
3894
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
3895
 
 
3896
                if (gtk_tree_selection_iter_is_selected(play_select, &iter) &&
 
3897
                    gtk_tree_model_iter_n_children(GTK_TREE_MODEL(play_store), &iter)) {
 
3898
 
 
3899
                        gtk_tree_view_get_cursor(GTK_TREE_VIEW(play_list), &path, &column);
 
3900
 
 
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);
 
3903
                        } else {
 
3904
                                gtk_tree_view_expand_row(GTK_TREE_VIEW(play_list), path, FALSE);
 
3905
                        }
 
3906
                        
 
3907
                        continue;
 
3908
                }
 
3909
 
 
3910
                j = 0;
 
3911
 
 
3912
                while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter_child, &iter, j++)) {
 
3913
 
 
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)) {
 
3916
 
 
3917
                                path = gtk_tree_model_get_path (GTK_TREE_MODEL(play_store), &iter);
 
3918
 
 
3919
                                if (path) {
 
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);
 
3922
                                }
 
3923
                        }
 
3924
                }
 
3925
        }
 
3926
}
 
3927
 
 
3928
 
 
3929
static gboolean
 
3930
pl_progress_bar_update(gpointer data) {
 
3931
 
 
3932
        if (pl_progress_bar != NULL) {
 
3933
                gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pl_progress_bar));
 
3934
                return TRUE;
 
3935
        }
 
3936
 
 
3937
        return FALSE;
 
3938
}
 
3939
 
 
3940
static void
 
3941
pl_progress_bar_stop_clicked(GtkWidget * widget, gpointer data) {
 
3942
 
 
3943
        playlist_thread_stop = 1;
 
3944
}
 
3945
 
 
3946
void
 
3947
playlist_progress_bar_show(void) {
 
3948
 
 
3949
        ++pl_progress_bar_semaphore;
 
3950
 
 
3951
        if (pl_progress_bar != NULL) {
 
3952
                return;
 
3953
        }
 
3954
 
 
3955
        playlist_thread_stop = 0;
 
3956
 
 
3957
        playlist_stats_set_busy();
 
3958
 
 
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);
 
3961
 
 
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);
 
3966
 
 
3967
        gtk_widget_show_all(pl_progress_bar_container);
 
3968
 
 
3969
        g_timeout_add(200, pl_progress_bar_update, NULL);
 
3970
}
 
3971
 
 
3972
 
 
3973
void
 
3974
playlist_progress_bar_hide(void) {
 
3975
 
 
3976
        if (pl_progress_bar != NULL) {
 
3977
                gtk_widget_destroy(pl_progress_bar);
 
3978
                pl_progress_bar = NULL;
 
3979
        }
 
3980
 
 
3981
        if (pl_progress_bar_stop_button != NULL) {
 
3982
                gtk_widget_destroy(pl_progress_bar_stop_button);
 
3983
                pl_progress_bar_stop_button = NULL;
 
3984
        }
 
3985
}
 
3986
 
 
3987
 
 
3988
void
 
3989
remove_files (void) {
 
3990
 
 
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;
 
3995
 
 
3996
        files = 0;
 
3997
 
 
3998
        while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i++)) {
 
3999
 
 
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);
 
4002
                        files++;
 
4003
                }
 
4004
 
 
4005
                j = 0;
 
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)) {
 
4008
                                n = 1;
 
4009
                        }
 
4010
                }
 
4011
                if (n) break;
 
4012
        }
 
4013
        
 
4014
        if (!n) {
 
4015
 
 
4016
                if (files == 1) {
 
4017
                        sprintf(temp, _("The selected file will be deleted from the filesystem. "
 
4018
                                        "No recovery will be possible after this operation.\n\n"
 
4019
                                        "Are you sure?"));
 
4020
                } else {
 
4021
                        sprintf(temp, _("All selected files will be deleted from the filesystem. "
 
4022
                                        "No recovery will be possible after this operation.\n\n"
 
4023
                                        "Are you sure?"));
 
4024
                }
 
4025
 
 
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);
 
4029
 
 
4030
                gtk_window_set_title(GTK_WINDOW(info_dialog), _("Remove file"));
 
4031
                gtk_widget_show (info_dialog);
 
4032
 
 
4033
                response = aqualung_dialog_run (GTK_DIALOG (info_dialog));     
 
4034
 
 
4035
                gtk_widget_destroy(info_dialog);
 
4036
 
 
4037
                if (response == GTK_RESPONSE_YES) {   
 
4038
 
 
4039
                        i = last_pos = 0;
 
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)) {
 
4042
 
 
4043
                                        char * filename_locale;
 
4044
 
 
4045
                                        gtk_tree_model_get(GTK_TREE_MODEL(play_store), &iter, COLUMN_PHYSICAL_FILENAME, &filename, -1);
 
4046
 
 
4047
                                        if ((filename_locale = g_locale_from_utf8(filename, -1, NULL, NULL, NULL)) == NULL) {
 
4048
                                                g_free(filename);
 
4049
                                                continue;
 
4050
                                        }
 
4051
 
 
4052
                                        n = unlink(filename_locale);
 
4053
                                        if (n != -1) {
 
4054
                                                gtk_tree_store_remove(play_store, &iter);
 
4055
                                                --i;
 
4056
                                                last_pos = (i-1) < 0 ? 0 : i-1;
 
4057
                                        }
 
4058
 
 
4059
                                        g_free(filename_locale);
 
4060
                                        g_free(filename);
 
4061
                                }
 
4062
                        }
 
4063
 
 
4064
                        for (i = 0; gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, i); i++);
 
4065
 
 
4066
                        if (i) {
 
4067
                                gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(play_store), &iter, NULL, last_pos);
 
4068
                                set_cursor_in_playlist(&iter, FALSE);
 
4069
                        }
 
4070
                        playlist_content_changed();
 
4071
                }
 
4072
        }
 
4073
}
 
4074
 
 
4075
void
 
4076
set_cursor_in_playlist (GtkTreeIter *iter, gboolean scroll) {
 
4077
 
 
4078
        GtkTreePath *visible_path;
 
4079
 
 
4080
        visible_path = gtk_tree_model_get_path (GTK_TREE_MODEL(play_store), iter);
 
4081
 
 
4082
        if (visible_path) {
 
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);
 
4087
                }
 
4088
                gtk_tree_path_free(visible_path);
 
4089
        }
 
4090
}
 
4091
 
 
4092
void
 
4093
select_active_position_in_playlist(void) {
 
4094
 
 
4095
        GtkTreeIter iter;
 
4096
        gchar * str;
 
4097
 
 
4098
        if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(play_store), &iter)) {
 
4099
 
 
4100
                do {
 
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);
 
4104
                                g_free(str);
 
4105
                                break;
 
4106
                        }
 
4107
                        g_free(str);
 
4108
                } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(play_store), &iter));
 
4109
        }
 
4110
}
 
4111
 
 
4112
// vim: shiftwidth=8:tabstop=8:softtabstop=8 :  
 
4113