00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <assert.h>
00023 #include <inttypes.h>
00024 #include <stdlib.h>
00025 #include <time.h>
00026
00027 #include <glib.h>
00028
00029 #include <libaudcore/audstrings.h>
00030 #include <libaudcore/hook.h>
00031 #include <libaudcore/tuple_formatter.h>
00032
00033 #include "audconfig.h"
00034 #include "config.h"
00035 #include "i18n.h"
00036 #include "main.h"
00037 #include "misc.h"
00038 #include "playback.h"
00039 #include "playlist.h"
00040 #include "playlist-utils.h"
00041 #include "plugin.h"
00042
00043 #define SCAN_DEBUG(...)
00044
00045 #define SCAN_THREADS 4
00046 #define STATE_FILE "playlist-state"
00047
00048 #define DECLARE_PLAYLIST \
00049 struct playlist * playlist
00050
00051 #define DECLARE_PLAYLIST_ENTRY \
00052 struct playlist * playlist; \
00053 struct entry * entry
00054
00055 #define LOOKUP_PLAYLIST \
00056 { \
00057 playlist = lookup_playlist (playlist_num); \
00058 g_return_if_fail (playlist != NULL); \
00059 }
00060
00061 #define LOOKUP_PLAYLIST_RET(ret) \
00062 { \
00063 playlist = lookup_playlist (playlist_num); \
00064 g_return_val_if_fail (playlist != NULL, ret); \
00065 }
00066
00067 #define LOOKUP_PLAYLIST_ENTRY \
00068 { \
00069 playlist = lookup_playlist (playlist_num); \
00070 g_return_if_fail (playlist != NULL); \
00071 entry = lookup_entry (playlist, entry_num); \
00072 g_return_if_fail (entry != NULL); \
00073 }
00074
00075 #define LOOKUP_PLAYLIST_ENTRY_RET(ret) \
00076 { \
00077 playlist = lookup_playlist (playlist_num); \
00078 g_return_val_if_fail (playlist != NULL, ret); \
00079 entry = lookup_entry (playlist, entry_num); \
00080 g_return_val_if_fail (entry != NULL, ret); \
00081 }
00082
00083 #define SELECTION_HAS_CHANGED \
00084 { \
00085 queue_update (PLAYLIST_UPDATE_SELECTION); \
00086 }
00087
00088 #define METADATA_WILL_CHANGE \
00089 { \
00090 scan_stop (); \
00091 }
00092
00093 #define METADATA_HAS_CHANGED \
00094 { \
00095 scan_reset (); \
00096 queue_update (PLAYLIST_UPDATE_METADATA); \
00097 }
00098
00099 #define PLAYLIST_WILL_CHANGE \
00100 { \
00101 scan_stop (); \
00102 }
00103
00104 #define PLAYLIST_HAS_CHANGED \
00105 { \
00106 scan_reset (); \
00107 queue_update (PLAYLIST_UPDATE_STRUCTURE); \
00108 }
00109
00110 struct entry
00111 {
00112 gint number;
00113 gchar *filename;
00114 InputPlugin *decoder;
00115 Tuple *tuple;
00116 gchar *title;
00117 gint length;
00118 gboolean failed;
00119 gboolean selected;
00120 gint shuffle_num;
00121 gboolean queued;
00122 gboolean segmented;
00123 gint start;
00124 gint end;
00125 };
00126
00127 struct playlist
00128 {
00129 gint number;
00130 gchar *filename;
00131 gchar *title;
00132 struct index *entries;
00133 struct entry *position;
00134 gint selected_count;
00135 gint last_shuffle_num;
00136 GList *queued;
00137 gint64 total_length;
00138 gint64 selected_length;
00139 };
00140
00141 static struct index *playlists;
00142 static struct playlist *active_playlist;
00143 static struct playlist *playing_playlist;
00144
00145 static gint update_source, update_level;
00146 static gint scan_source;
00147 static GMutex * scan_mutex;
00148 static GCond * scan_conds[SCAN_THREADS];
00149 static const gchar * scan_filenames[SCAN_THREADS];
00150 static InputPlugin * scan_decoders[SCAN_THREADS];
00151 static Tuple * scan_tuples[SCAN_THREADS];
00152 static gboolean scan_quit;
00153 static GThread * scan_threads[SCAN_THREADS];
00154 static gint scan_positions[SCAN_THREADS];
00155 gint updated_ago;
00156
00157 static void * scanner (void * unused);
00158
00159 static gchar *title_from_tuple(Tuple * tuple)
00160 {
00161 const gchar *format = tuple_get_string(tuple, FIELD_FORMATTER, NULL);
00162
00163 if (format == NULL)
00164 format = get_gentitle_format();
00165
00166 return tuple_formatter_make_title_string(tuple, format);
00167 }
00168
00169 static void entry_set_tuple_real (struct entry * entry, Tuple * tuple)
00170 {
00171
00172
00173 if (entry->segmented)
00174 {
00175 if (tuple != NULL)
00176 tuple_free (tuple);
00177
00178 return;
00179 }
00180
00181 if (entry->tuple != NULL)
00182 tuple_free (entry->tuple);
00183
00184 g_free (entry->title);
00185 entry->tuple = tuple;
00186
00187 if (tuple == NULL)
00188 {
00189 entry->title = NULL;
00190 entry->length = 0;
00191 entry->segmented = FALSE;
00192 entry->start = entry->end = -1;
00193 }
00194 else
00195 {
00196 entry->title = title_from_tuple (tuple);
00197 entry->length = tuple_get_int (tuple, FIELD_LENGTH, NULL);
00198 entry->length = MAX (entry->length, 0);
00199
00200 if (tuple_get_value_type (tuple, FIELD_SEGMENT_START, NULL) == TUPLE_INT)
00201 {
00202 entry->segmented = TRUE;
00203 entry->start = tuple_get_int (tuple, FIELD_SEGMENT_START, NULL);
00204
00205 if (tuple_get_value_type (tuple, FIELD_SEGMENT_END, NULL) ==
00206 TUPLE_INT)
00207 entry->end = tuple_get_int (tuple, FIELD_SEGMENT_END, NULL);
00208 else
00209 entry->end = -1;
00210 }
00211 else
00212 entry->segmented = FALSE;
00213 }
00214 }
00215
00216 static void entry_set_tuple (struct playlist * playlist, struct entry * entry,
00217 Tuple * tuple)
00218 {
00219 if (entry->tuple != NULL)
00220 {
00221 playlist->total_length -= entry->length;
00222
00223 if (entry->selected)
00224 playlist->selected_length -= entry->length;
00225 }
00226
00227 entry_set_tuple_real (entry, tuple);
00228
00229 if (tuple != NULL)
00230 {
00231 playlist->total_length += entry->length;
00232
00233 if (entry->selected)
00234 playlist->selected_length += entry->length;
00235 }
00236 }
00237
00238 static void entry_set_failed (struct playlist * playlist, struct entry * entry)
00239 {
00240 entry_set_tuple (playlist, entry, tuple_new_from_filename (entry->filename));
00241 entry->failed = TRUE;
00242 }
00243
00244 static struct entry *entry_new(gchar * filename, InputPlugin * decoder, Tuple * tuple)
00245 {
00246 struct entry *entry = g_malloc(sizeof(struct entry));
00247
00248 entry->filename = filename;
00249 entry->decoder = decoder;
00250 entry->tuple = NULL;
00251 entry->title = NULL;
00252 entry->failed = FALSE;
00253 entry->number = -1;
00254 entry->selected = FALSE;
00255 entry->shuffle_num = 0;
00256 entry->queued = FALSE;
00257 entry->segmented = FALSE;
00258 entry->start = entry->end = -1;
00259
00260 entry_set_tuple_real (entry, tuple);
00261 return entry;
00262 }
00263
00264 static void entry_free(struct entry *entry)
00265 {
00266 g_free(entry->filename);
00267
00268 if (entry->tuple != NULL)
00269 tuple_free(entry->tuple);
00270
00271 g_free(entry->title);
00272 g_free(entry);
00273 }
00274
00275 static void entry_check_has_decoder (struct playlist * playlist, struct entry *
00276 entry)
00277 {
00278 if (entry->decoder != NULL || entry->failed)
00279 return;
00280
00281 entry->decoder = file_find_decoder (entry->filename, FALSE);
00282 if (! entry->decoder)
00283 entry_set_failed (playlist, entry);
00284 }
00285
00286 static struct playlist *playlist_new(void)
00287 {
00288 struct playlist *playlist = g_malloc(sizeof(struct playlist));
00289
00290 playlist->number = -1;
00291 playlist->filename = NULL;
00292 playlist->title = g_strdup(_("Untitled Playlist"));
00293 playlist->entries = index_new();
00294 playlist->position = NULL;
00295 playlist->selected_count = 0;
00296 playlist->last_shuffle_num = 0;
00297 playlist->queued = NULL;
00298 playlist->total_length = 0;
00299 playlist->selected_length = 0;
00300
00301 return playlist;
00302 }
00303
00304 static void playlist_free(struct playlist *playlist)
00305 {
00306 gint count;
00307
00308 g_free(playlist->filename);
00309 g_free(playlist->title);
00310
00311 for (count = 0; count < index_count(playlist->entries); count++)
00312 entry_free(index_get(playlist->entries, count));
00313
00314 index_free(playlist->entries);
00315 g_list_free(playlist->queued);
00316 g_free(playlist);
00317 }
00318
00319 static void number_playlists(gint at, gint length)
00320 {
00321 gint count;
00322
00323 for (count = 0; count < length; count++)
00324 {
00325 struct playlist *playlist = index_get(playlists, at + count);
00326
00327 playlist->number = at + count;
00328 }
00329 }
00330
00331 static struct playlist *lookup_playlist(gint playlist_num)
00332 {
00333 if (playlist_num < 0 || playlist_num >= index_count(playlists))
00334 return NULL;
00335
00336 return index_get(playlists, playlist_num);
00337 }
00338
00339 static void number_entries(struct playlist *playlist, gint at, gint length)
00340 {
00341 gint count;
00342
00343 for (count = 0; count < length; count++)
00344 {
00345 struct entry *entry = index_get(playlist->entries, at + count);
00346
00347 entry->number = at + count;
00348 }
00349 }
00350
00351 static struct entry *lookup_entry(struct playlist *playlist, gint entry_num)
00352 {
00353 if (entry_num < 0 || entry_num >= index_count(playlist->entries))
00354 return NULL;
00355
00356 return index_get(playlist->entries, entry_num);
00357 }
00358
00359 static gboolean update (void * unused)
00360 {
00361 hook_call ("playlist update", GINT_TO_POINTER (update_level));
00362
00363 update_source = 0;
00364 update_level = 0;
00365 return FALSE;
00366 }
00367
00368 static void queue_update (gint level)
00369 {
00370 update_level = MAX (update_level, level);
00371
00372 if (update_source == 0)
00373 update_source = g_idle_add_full (G_PRIORITY_HIGH_IDLE, update, NULL,
00374 NULL);
00375 }
00376
00377
00378 void scan_receive (void)
00379 {
00380 for (gint i = 0; i < SCAN_THREADS; i ++)
00381 {
00382 struct entry * entry;
00383
00384 if (! scan_filenames[i] || scan_decoders[i])
00385 continue;
00386
00387 SCAN_DEBUG ("receive (#%d): %d\n", i, scan_positions[i]);
00388 entry = index_get (active_playlist->entries, scan_positions[i]);
00389
00390 if (scan_tuples[i])
00391 entry_set_tuple (active_playlist, entry, scan_tuples[i]);
00392 else
00393 entry_set_failed (active_playlist, entry);
00394
00395 scan_filenames[i] = NULL;
00396 scan_tuples[i] = NULL;
00397
00398 updated_ago ++;
00399 }
00400 }
00401
00402 static gboolean scan_next (void * unused)
00403 {
00404 gint entries = index_count (active_playlist->entries);
00405 gint search = 0;
00406
00407 g_mutex_lock (scan_mutex);
00408
00409 if (scan_source)
00410 {
00411 g_source_remove (scan_source);
00412 scan_source = 0;
00413 }
00414
00415 scan_receive ();
00416
00417 for (gint i = 0; i < SCAN_THREADS; i ++)
00418 search = MAX (search, scan_positions[i] + 1);
00419
00420 for (gint i = 0; i < SCAN_THREADS; i ++)
00421 {
00422 if (scan_filenames[i])
00423 continue;
00424
00425 for (; search < entries; search ++)
00426 {
00427 struct entry * entry = index_get (active_playlist->entries, search);
00428
00429 if (entry->tuple)
00430 continue;
00431
00432 entry_check_has_decoder (active_playlist, entry);
00433 if (entry->failed)
00434 continue;
00435
00436 SCAN_DEBUG ("start (#%d): %d\n", i, search);
00437 scan_positions[i] = search;
00438 scan_filenames[i] = entry->filename;
00439 scan_decoders[i] = entry->decoder;
00440 g_cond_signal (scan_conds[i]);
00441
00442 search ++;
00443 break;
00444 }
00445 }
00446
00447 if (updated_ago >= 10 || (search == entries && updated_ago > 0))
00448 {
00449 SCAN_DEBUG ("queue update\n");
00450 queue_update (PLAYLIST_UPDATE_METADATA);
00451 updated_ago = 0;
00452 }
00453
00454 g_mutex_unlock (scan_mutex);
00455 return FALSE;
00456 }
00457
00458 static void scan_continue (void)
00459 {
00460 SCAN_DEBUG ("scan_continue\n");
00461 if (! scan_source)
00462 scan_source = g_idle_add_full (G_PRIORITY_LOW, scan_next, NULL, NULL);
00463 }
00464
00465 static void scan_reset (void)
00466 {
00467 SCAN_DEBUG ("scan_reset\n");
00468
00469 for (gint i = 0; i < SCAN_THREADS; i ++)
00470 {
00471 assert (! scan_filenames[i]);
00472 scan_positions[i] = -1;
00473 }
00474
00475 updated_ago = 0;
00476 scan_continue ();
00477 }
00478
00479 static void scan_stop (void)
00480 {
00481 SCAN_DEBUG ("scan_stop\n");
00482 g_mutex_lock (scan_mutex);
00483
00484 if (scan_source != 0)
00485 {
00486 g_source_remove (scan_source);
00487 scan_source = 0;
00488 }
00489
00490 for (gint i = 0; i < SCAN_THREADS; i ++)
00491 {
00492 if (! scan_filenames[i])
00493 continue;
00494
00495 while (scan_decoders[i])
00496 {
00497 SCAN_DEBUG ("wait for stop (#%d)\n", i);
00498 g_cond_wait (scan_conds[i], scan_mutex);
00499 }
00500 }
00501
00502 scan_receive ();
00503 g_mutex_unlock (scan_mutex);
00504 }
00505
00506 static void * scanner (void * data)
00507 {
00508 gint i = GPOINTER_TO_INT (data);
00509
00510 g_mutex_lock (scan_mutex);
00511 g_cond_signal (scan_conds[i]);
00512
00513 while (1)
00514 {
00515 SCAN_DEBUG ("scanner (#%d): wait\n", i);
00516 g_cond_wait (scan_conds[i], scan_mutex);
00517
00518 if (scan_quit)
00519 break;
00520
00521 if (! scan_filenames[i])
00522 {
00523 SCAN_DEBUG ("scanner (#%d): idle\n", i);
00524 continue;
00525 }
00526
00527 SCAN_DEBUG ("scanner (#%d): scan %s\n", i, scan_filenames[i]);
00528 scan_tuples[i] = file_read_tuple (scan_filenames[i], scan_decoders[i]);
00529 scan_decoders[i] = NULL;
00530 g_cond_signal (scan_conds[i]);
00531 scan_continue ();
00532 }
00533
00534 SCAN_DEBUG ("scanner (#%d): exit\n", i);
00535 g_mutex_unlock (scan_mutex);
00536 return NULL;
00537 }
00538
00539
00540
00541
00542
00543
00544 static gboolean scan_threaded (struct playlist * playlist, struct entry * entry)
00545 {
00546 gint i;
00547
00548 if (playlist != active_playlist)
00549 return FALSE;
00550
00551 scan_next (NULL);
00552
00553 if (entry->tuple)
00554 return TRUE;
00555
00556 for (i = 0; i < SCAN_THREADS; i ++)
00557 {
00558 if (entry->number == scan_positions[i])
00559 goto FOUND;
00560 }
00561
00562 SCAN_DEBUG ("manual scan of %d\n", entry->number);
00563 return FALSE;
00564
00565 FOUND:
00566 SCAN_DEBUG ("threaded scan (#%d) of %d\n", i, entry->number);
00567 g_mutex_lock (scan_mutex);
00568 scan_receive ();
00569
00570 while (scan_filenames[i])
00571 {
00572 SCAN_DEBUG ("wait (#%d) for %d\n", i, entry->number);
00573 g_cond_wait (scan_conds[i], scan_mutex);
00574 scan_receive ();
00575 }
00576
00577 g_mutex_unlock (scan_mutex);
00578 return TRUE;
00579 }
00580
00581 static void check_scanned (struct playlist * playlist, struct entry * entry)
00582 {
00583 if (entry->tuple)
00584 return;
00585 if (scan_threaded (playlist, entry))
00586 return;
00587
00588 entry_check_has_decoder (playlist, entry);
00589 if (entry->failed)
00590 return;
00591
00592 entry_set_tuple (playlist, entry, file_read_tuple (entry->filename,
00593 entry->decoder));
00594 if (! entry->tuple)
00595 entry_set_failed (playlist, entry);
00596
00597 queue_update (PLAYLIST_UPDATE_METADATA);
00598 }
00599
00600 static void check_selected_scanned (struct playlist * playlist)
00601 {
00602 gint entries = index_count (playlist->entries);
00603 for (gint count = 0; count < entries; count++)
00604 {
00605 struct entry * entry = index_get (playlist->entries, count);
00606 if (entry->selected)
00607 check_scanned (playlist, entry);
00608 }
00609 }
00610
00611 static void check_all_scanned (struct playlist * playlist)
00612 {
00613 gint entries = index_count (playlist->entries);
00614 for (gint count = 0; count < entries; count++)
00615 check_scanned (playlist, index_get (playlist->entries, count));
00616 }
00617
00618 void playlist_init (void)
00619 {
00620 struct playlist * playlist;
00621
00622 srandom (time (NULL));
00623
00624 playlists = index_new ();
00625 playlist = playlist_new ();
00626 index_append (playlists, playlist);
00627 playlist->number = 0;
00628 active_playlist = playlist;
00629 playing_playlist = NULL;
00630
00631 update_source = 0;
00632 update_level = 0;
00633
00634 scan_mutex = g_mutex_new ();
00635 memset (scan_filenames, 0, sizeof scan_filenames);
00636 memset (scan_decoders, 0, sizeof scan_decoders);
00637 memset (scan_tuples, 0, sizeof scan_tuples);
00638 scan_source = 0;
00639 scan_quit = FALSE;
00640
00641 g_mutex_lock (scan_mutex);
00642
00643 for (gint i = 0; i < SCAN_THREADS; i ++)
00644 {
00645 scan_conds[i] = g_cond_new ();
00646 scan_threads[i] = g_thread_create (scanner, GINT_TO_POINTER (i), TRUE,
00647 NULL);
00648 g_cond_wait (scan_conds[i], scan_mutex);
00649 }
00650
00651 g_mutex_unlock (scan_mutex);
00652
00653 scan_reset ();
00654 }
00655
00656 void playlist_end(void)
00657 {
00658 gint count;
00659
00660 scan_stop ();
00661 scan_quit = TRUE;
00662
00663 for (gint i = 0; i < SCAN_THREADS; i ++)
00664 {
00665 g_mutex_lock (scan_mutex);
00666 g_cond_signal (scan_conds[i]);
00667 g_mutex_unlock (scan_mutex);
00668 g_thread_join (scan_threads[i]);
00669 g_cond_free (scan_conds[i]);
00670 }
00671
00672 g_mutex_free (scan_mutex);
00673
00674 if (update_source != 0)
00675 g_source_remove(update_source);
00676
00677 for (count = 0; count < index_count(playlists); count++)
00678 playlist_free(index_get(playlists, count));
00679
00680 index_free(playlists);
00681 }
00682
00683 gint playlist_count(void)
00684 {
00685 return index_count(playlists);
00686 }
00687
00688 void playlist_insert(gint at)
00689 {
00690 PLAYLIST_WILL_CHANGE;
00691
00692 if (at < 0 || at > index_count(playlists))
00693 at = index_count(playlists);
00694
00695 if (at == index_count(playlists))
00696 index_append(playlists, playlist_new());
00697 else
00698 index_insert(playlists, at, playlist_new());
00699
00700 number_playlists(at, index_count(playlists) - at);
00701
00702 PLAYLIST_HAS_CHANGED;
00703 hook_call ("playlist insert", GINT_TO_POINTER (at));
00704 }
00705
00706 void playlist_reorder (gint from, gint to, gint count)
00707 {
00708 struct index * displaced;
00709
00710 g_return_if_fail (from >= 0 && from + count <= index_count (playlists));
00711 g_return_if_fail (to >= 0 && to + count <= index_count (playlists));
00712 g_return_if_fail (count >= 0);
00713
00714 PLAYLIST_WILL_CHANGE;
00715
00716 displaced = index_new ();
00717
00718 if (to < from)
00719 index_copy_append (playlists, to, displaced, from - to);
00720 else
00721 index_copy_append (playlists, from + count, displaced, to - from);
00722
00723 index_move (playlists, from, to, count);
00724
00725 if (to < from)
00726 {
00727 index_copy_set (displaced, 0, playlists, to + count, from - to);
00728 number_playlists (to, from + count - to);
00729 }
00730 else
00731 {
00732 index_copy_set (displaced, 0, playlists, from, to - from);
00733 number_playlists (from, to + count - from);
00734 }
00735
00736 index_free (displaced);
00737
00738 PLAYLIST_HAS_CHANGED;
00739 }
00740
00741 void playlist_delete (gint playlist_num)
00742 {
00743 DECLARE_PLAYLIST;
00744
00745 LOOKUP_PLAYLIST;
00746
00747 hook_call ("playlist delete", GINT_TO_POINTER (playlist_num));
00748
00749 if (playlist == playing_playlist)
00750 {
00751 if (playback_get_playing ())
00752 playback_stop ();
00753
00754 playing_playlist = NULL;
00755 }
00756
00757 PLAYLIST_WILL_CHANGE;
00758
00759 playlist_free(playlist);
00760 index_delete(playlists, playlist_num, 1);
00761 number_playlists(playlist_num, index_count(playlists) - playlist_num);
00762
00763 if (index_count(playlists) == 0)
00764 playlist_insert(0);
00765
00766 if (playlist == active_playlist)
00767 active_playlist = index_get (playlists, MIN (playlist_num, index_count
00768 (playlists) - 1));
00769
00770 PLAYLIST_HAS_CHANGED;
00771 }
00772
00773 void playlist_set_filename(gint playlist_num, const gchar * filename)
00774 {
00775 DECLARE_PLAYLIST;
00776
00777 LOOKUP_PLAYLIST;
00778 PLAYLIST_WILL_CHANGE;
00779
00780 g_free(playlist->filename);
00781 playlist->filename = g_strdup(filename);
00782
00783 PLAYLIST_HAS_CHANGED;
00784 }
00785
00786 const gchar *playlist_get_filename(gint playlist_num)
00787 {
00788 DECLARE_PLAYLIST;
00789
00790 LOOKUP_PLAYLIST_RET (NULL);
00791
00792 return playlist->filename;
00793 }
00794
00795 void playlist_set_title(gint playlist_num, const gchar * title)
00796 {
00797 DECLARE_PLAYLIST;
00798
00799 LOOKUP_PLAYLIST;
00800 PLAYLIST_WILL_CHANGE;
00801
00802 g_free(playlist->title);
00803 playlist->title = g_strdup(title);
00804
00805 PLAYLIST_HAS_CHANGED;
00806 }
00807
00808 const gchar *playlist_get_title(gint playlist_num)
00809 {
00810 DECLARE_PLAYLIST;
00811
00812 LOOKUP_PLAYLIST_RET (NULL);
00813
00814 return playlist->title;
00815 }
00816
00817 void playlist_set_active(gint playlist_num)
00818 {
00819 DECLARE_PLAYLIST;
00820
00821 LOOKUP_PLAYLIST;
00822 PLAYLIST_WILL_CHANGE;
00823
00824 active_playlist = playlist;
00825
00826 PLAYLIST_HAS_CHANGED;
00827 }
00828
00829 gint playlist_get_active(void)
00830 {
00831 return (active_playlist == NULL) ? -1 : active_playlist->number;
00832 }
00833
00834 void playlist_set_playing(gint playlist_num)
00835 {
00836 DECLARE_PLAYLIST;
00837
00838 if (playlist_num == -1)
00839 playlist = NULL;
00840 else
00841 LOOKUP_PLAYLIST;
00842
00843 if (playing_playlist != NULL && playback_get_playing ())
00844 playback_stop();
00845
00846 playing_playlist = playlist;
00847 }
00848
00849 gint playlist_get_playing(void)
00850 {
00851 return (playing_playlist == NULL) ? -1 : playing_playlist->number;
00852 }
00853
00854
00855
00856 static void set_position (struct playlist * playlist, struct entry * entry)
00857 {
00858 if (entry == playlist->position)
00859 return;
00860
00861 playlist->position = entry;
00862
00863 if (entry == NULL)
00864 return;
00865
00866 if (! entry->shuffle_num || entry->shuffle_num != playlist->last_shuffle_num)
00867 {
00868 playlist->last_shuffle_num ++;
00869 entry->shuffle_num = playlist->last_shuffle_num;
00870 }
00871 }
00872
00873 gint playlist_entry_count(gint playlist_num)
00874 {
00875 DECLARE_PLAYLIST;
00876
00877 LOOKUP_PLAYLIST_RET (0);
00878
00879 return index_count(playlist->entries);
00880 }
00881
00882 static void make_entries (gchar * filename, InputPlugin * decoder, Tuple *
00883 tuple, struct index * list)
00884 {
00885 uri_check_utf8 (& filename, TRUE);
00886
00887 if (tuple == NULL && decoder == NULL)
00888 decoder = file_find_decoder (filename, TRUE);
00889
00890 if (tuple == NULL && decoder != NULL && decoder->have_subtune && strchr
00891 (filename, '?') == NULL)
00892 tuple = file_read_tuple (filename, decoder);
00893
00894 if (tuple != NULL && tuple->nsubtunes > 0)
00895 {
00896 gint subtune;
00897
00898 for (subtune = 0; subtune < tuple->nsubtunes; subtune++)
00899 {
00900 gchar *name = g_strdup_printf("%s?%d", filename, (tuple->subtunes == NULL) ? 1 + subtune : tuple->subtunes[subtune]);
00901 make_entries(name, decoder, NULL, list);
00902 }
00903
00904 g_free(filename);
00905 tuple_free(tuple);
00906 }
00907 else
00908 index_append(list, entry_new(filename, decoder, tuple));
00909 }
00910
00911 void playlist_entry_insert(gint playlist_num, gint at, gchar * filename, Tuple * tuple)
00912 {
00913 struct index *filenames = index_new();
00914 struct index *tuples = index_new();
00915
00916 index_append(filenames, filename);
00917 index_append(tuples, tuple);
00918
00919 playlist_entry_insert_batch(playlist_num, at, filenames, tuples);
00920 }
00921
00922 void playlist_entry_insert_batch(gint playlist_num, gint at, struct index *filenames, struct index *tuples)
00923 {
00924 DECLARE_PLAYLIST;
00925 gint entries, number, count;
00926 struct index *add;
00927
00928 LOOKUP_PLAYLIST;
00929 PLAYLIST_WILL_CHANGE;
00930
00931 entries = index_count (playlist->entries);
00932
00933 if (at < 0 || at > entries)
00934 at = entries;
00935
00936 number = index_count(filenames);
00937 add = index_new();
00938
00939 for (count = 0; count < number; count++)
00940 make_entries(index_get(filenames, count), NULL, (tuples == NULL) ? NULL : index_get(tuples, count), add);
00941
00942 index_free(filenames);
00943
00944 if (tuples != NULL)
00945 index_free(tuples);
00946
00947 number = index_count(add);
00948
00949 if (at == entries)
00950 index_merge_append(playlist->entries, add);
00951 else
00952 index_merge_insert(playlist->entries, at, add);
00953
00954 index_free(add);
00955
00956 number_entries(playlist, at, entries + number - at);
00957
00958 for (count = 0; count < number; count++)
00959 {
00960 struct entry *entry = index_get(playlist->entries, at + count);
00961
00962 playlist->total_length += entry->length;
00963 }
00964
00965 PLAYLIST_HAS_CHANGED;
00966 }
00967
00968 void playlist_entry_delete(gint playlist_num, gint at, gint number)
00969 {
00970 DECLARE_PLAYLIST;
00971 gboolean stop = FALSE;
00972 gint entries, count;
00973
00974 LOOKUP_PLAYLIST;
00975 PLAYLIST_WILL_CHANGE;
00976
00977 entries = index_count (playlist->entries);
00978
00979 if (at < 0 || at > entries)
00980 at = entries;
00981 if (number < 0 || number > entries - at)
00982 number = entries - at;
00983
00984 for (count = 0; count < number; count++)
00985 {
00986 struct entry *entry = index_get(playlist->entries, at + count);
00987
00988 if (entry == playlist->position)
00989 {
00990 stop = (playlist == playing_playlist);
00991 set_position (playlist, NULL);
00992 }
00993
00994 if (entry->selected)
00995 {
00996 playlist->selected_count--;
00997 playlist->selected_length -= entry->length;
00998 }
00999
01000 if (entry->queued)
01001 playlist->queued = g_list_remove(playlist->queued, entry);
01002
01003 playlist->total_length -= entry->length;
01004
01005 entry_free(entry);
01006 }
01007
01008 index_delete(playlist->entries, at, number);
01009 number_entries(playlist, at, entries - number - at);
01010
01011 if (stop && playback_get_playing ())
01012 playback_stop ();
01013
01014 PLAYLIST_HAS_CHANGED;
01015 }
01016
01017 const gchar *playlist_entry_get_filename(gint playlist_num, gint entry_num)
01018 {
01019 DECLARE_PLAYLIST_ENTRY;
01020
01021 LOOKUP_PLAYLIST_ENTRY_RET (NULL);
01022
01023 return entry->filename;
01024 }
01025
01026 InputPlugin *playlist_entry_get_decoder(gint playlist_num, gint entry_num)
01027 {
01028 DECLARE_PLAYLIST_ENTRY;
01029
01030 LOOKUP_PLAYLIST_ENTRY_RET (NULL);
01031
01032 entry_check_has_decoder (playlist, entry);
01033
01034 return entry->decoder;
01035 }
01036
01037 void playlist_entry_set_tuple (gint playlist_num, gint entry_num, Tuple * tuple)
01038 {
01039 DECLARE_PLAYLIST_ENTRY;
01040
01041 LOOKUP_PLAYLIST_ENTRY;
01042 METADATA_WILL_CHANGE;
01043
01044 entry_set_tuple (playlist, entry, tuple);
01045
01046 METADATA_HAS_CHANGED;
01047 }
01048
01049 const Tuple * playlist_entry_get_tuple (gint playlist_num, gint entry_num,
01050 gboolean fast)
01051 {
01052 DECLARE_PLAYLIST_ENTRY;
01053 LOOKUP_PLAYLIST_ENTRY_RET (NULL);
01054
01055 if (! fast)
01056 check_scanned (playlist, entry);
01057
01058 return entry->tuple;
01059 }
01060
01061 const gchar * playlist_entry_get_title (gint playlist_num, gint entry_num,
01062 gboolean fast)
01063 {
01064 DECLARE_PLAYLIST_ENTRY;
01065 LOOKUP_PLAYLIST_ENTRY_RET (NULL);
01066
01067 if (! fast)
01068 check_scanned (playlist, entry);
01069
01070 return (entry->title == NULL) ? entry->filename : entry->title;
01071 }
01072
01073 gint playlist_entry_get_length (gint playlist_num, gint entry_num, gboolean fast)
01074 {
01075 DECLARE_PLAYLIST_ENTRY;
01076 LOOKUP_PLAYLIST_ENTRY_RET (0);
01077
01078 if (! fast)
01079 check_scanned (playlist, entry);
01080
01081 return entry->length;
01082 }
01083
01084 gboolean playlist_entry_is_segmented(gint playlist_num, gint entry_num)
01085 {
01086 DECLARE_PLAYLIST_ENTRY;
01087
01088 LOOKUP_PLAYLIST_ENTRY_RET (FALSE);
01089
01090 return entry->segmented;
01091 }
01092
01093 gint playlist_entry_get_start_time (gint playlist_num, gint entry_num)
01094 {
01095 DECLARE_PLAYLIST_ENTRY;
01096
01097 LOOKUP_PLAYLIST_ENTRY_RET (-1);
01098
01099 return entry->start;
01100 }
01101
01102 gint playlist_entry_get_end_time (gint playlist_num, gint entry_num)
01103 {
01104 DECLARE_PLAYLIST_ENTRY;
01105
01106 LOOKUP_PLAYLIST_ENTRY_RET (-1);
01107
01108 return entry->end;
01109 }
01110
01111 void playlist_set_position (gint playlist_num, gint entry_num)
01112 {
01113 DECLARE_PLAYLIST_ENTRY;
01114
01115 if (entry_num == -1)
01116 {
01117 LOOKUP_PLAYLIST;
01118 entry = NULL;
01119 }
01120 else
01121 LOOKUP_PLAYLIST_ENTRY;
01122
01123 if (playlist == playing_playlist && playback_get_playing ())
01124 playback_stop ();
01125
01126 set_position (playlist, entry);
01127
01128 SELECTION_HAS_CHANGED;
01129
01130 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
01131 }
01132
01133 gint playlist_get_position(gint playlist_num)
01134 {
01135 DECLARE_PLAYLIST;
01136
01137 LOOKUP_PLAYLIST_RET (-1);
01138
01139 return (playlist->position == NULL) ? -1 : playlist->position->number;
01140 }
01141
01142 void playlist_entry_set_selected(gint playlist_num, gint entry_num, gboolean selected)
01143 {
01144 DECLARE_PLAYLIST_ENTRY;
01145
01146 LOOKUP_PLAYLIST_ENTRY;
01147
01148 if (entry->selected == selected)
01149 return;
01150
01151 entry->selected = selected;
01152
01153 if (selected)
01154 {
01155 playlist->selected_count++;
01156 playlist->selected_length += entry->length;
01157 }
01158 else
01159 {
01160 playlist->selected_count--;
01161 playlist->selected_length -= entry->length;
01162 }
01163
01164 SELECTION_HAS_CHANGED;
01165 }
01166
01167 gboolean playlist_entry_get_selected(gint playlist_num, gint entry_num)
01168 {
01169 DECLARE_PLAYLIST_ENTRY;
01170
01171 LOOKUP_PLAYLIST_ENTRY_RET (FALSE);
01172
01173 return entry->selected;
01174 }
01175
01176 gint playlist_selected_count(gint playlist_num)
01177 {
01178 DECLARE_PLAYLIST;
01179
01180 LOOKUP_PLAYLIST_RET (0);
01181
01182 return playlist->selected_count;
01183 }
01184
01185 void playlist_select_all(gint playlist_num, gboolean selected)
01186 {
01187 DECLARE_PLAYLIST;
01188 gint entries, count;
01189
01190 LOOKUP_PLAYLIST;
01191
01192 entries = index_count(playlist->entries);
01193
01194 for (count = 0; count < entries; count++)
01195 {
01196 struct entry *entry = index_get(playlist->entries, count);
01197
01198 entry->selected = selected;
01199 }
01200
01201 if (selected)
01202 {
01203 playlist->selected_count = entries;
01204 playlist->selected_length = playlist->total_length;
01205 }
01206 else
01207 {
01208 playlist->selected_count = 0;
01209 playlist->selected_length = 0;
01210 }
01211
01212 SELECTION_HAS_CHANGED;
01213 }
01214
01215 gint playlist_shift (gint playlist_num, gint entry_num, gint distance)
01216 {
01217 DECLARE_PLAYLIST_ENTRY;
01218 gint entries, first, last, shift, count;
01219 struct index *move, *others;
01220
01221 LOOKUP_PLAYLIST_ENTRY_RET (0);
01222
01223 if (! entry->selected)
01224 return 0;
01225
01226 PLAYLIST_WILL_CHANGE;
01227
01228 entries = index_count(playlist->entries);
01229 shift = 0;
01230
01231 for (first = entry_num; first > 0; first--)
01232 {
01233 entry = index_get(playlist->entries, first - 1);
01234
01235 if (!entry->selected)
01236 {
01237 if (shift <= distance)
01238 break;
01239
01240 shift--;
01241 }
01242 }
01243
01244 for (last = entry_num; last < entries - 1; last++)
01245 {
01246 entry = index_get(playlist->entries, last + 1);
01247
01248 if (!entry->selected)
01249 {
01250 if (shift >= distance)
01251 break;
01252
01253 shift++;
01254 }
01255 }
01256
01257 move = index_new();
01258 others = index_new();
01259
01260 for (count = first; count <= last; count++)
01261 {
01262 entry = index_get(playlist->entries, count);
01263 index_append(entry->selected ? move : others, entry);
01264 }
01265
01266 if (shift < 0)
01267 {
01268 index_merge_append(move, others);
01269 index_free(others);
01270 }
01271 else
01272 {
01273 index_merge_append(others, move);
01274 index_free(move);
01275 move = others;
01276 }
01277
01278 for (count = first; count <= last; count++)
01279 index_set(playlist->entries, count, index_get(move, count - first));
01280
01281 index_free(move);
01282
01283 number_entries(playlist, first, 1 + last - first);
01284
01285 PLAYLIST_HAS_CHANGED;
01286 return shift;
01287 }
01288
01289 void playlist_delete_selected(gint playlist_num)
01290 {
01291 DECLARE_PLAYLIST;
01292 gboolean stop = FALSE;
01293 gint entries, count;
01294 struct index *others;
01295
01296 LOOKUP_PLAYLIST;
01297 PLAYLIST_WILL_CHANGE;
01298
01299 entries = index_count (playlist->entries);
01300 others = index_new();
01301
01302 for (count = 0; count < entries; count++)
01303 {
01304 struct entry *entry = index_get(playlist->entries, count);
01305
01306 if (entry->selected)
01307 {
01308 if (entry == playlist->position)
01309 {
01310 stop = (playlist == playing_playlist);
01311 set_position (playlist, NULL);
01312 }
01313
01314 if (entry->queued)
01315 playlist->queued = g_list_remove(playlist->queued, entry);
01316
01317 playlist->total_length -= entry->length;
01318
01319 entry_free(entry);
01320 }
01321 else
01322 index_append(others, entry);
01323 }
01324
01325 index_free(playlist->entries);
01326 playlist->entries = others;
01327
01328 number_entries(playlist, 0, index_count(playlist->entries));
01329 playlist->selected_count = 0;
01330 playlist->selected_length = 0;
01331
01332 if (stop && playback_get_playing ())
01333 playback_stop ();
01334
01335 PLAYLIST_HAS_CHANGED;
01336 }
01337
01338 void playlist_reverse(gint playlist_num)
01339 {
01340 DECLARE_PLAYLIST;
01341 gint entries, count;
01342 struct index *reversed;
01343
01344 LOOKUP_PLAYLIST;
01345 PLAYLIST_WILL_CHANGE;
01346
01347 entries = index_count(playlist->entries);
01348 reversed = index_new();
01349 count = entries;
01350
01351 while (count--)
01352 index_append(reversed, index_get(playlist->entries, count));
01353
01354 index_free(playlist->entries);
01355 playlist->entries = reversed;
01356
01357 number_entries(playlist, 0, entries);
01358
01359 PLAYLIST_HAS_CHANGED;
01360 }
01361
01362 void playlist_randomize (gint playlist_num)
01363 {
01364 DECLARE_PLAYLIST;
01365 LOOKUP_PLAYLIST;
01366 PLAYLIST_WILL_CHANGE;
01367
01368 gint entries = index_count (playlist->entries);
01369
01370 for (gint i = 0; i < entries; i ++)
01371 {
01372 gint j = i + random () % (entries - i);
01373
01374 struct entry * entry = index_get (playlist->entries, j);
01375 index_set (playlist->entries, j, index_get (playlist->entries, i));
01376 index_set (playlist->entries, i, entry);
01377 }
01378
01379 number_entries (playlist, 0, entries);
01380 PLAYLIST_HAS_CHANGED;
01381 }
01382
01383 static gint filename_compare (const void * _a, const void * _b, void * _compare)
01384 {
01385 const struct entry * a = _a, * b = _b;
01386 gint (* compare) (const gchar * a, const gchar * b) = _compare;
01387
01388 gint diff = compare (a->filename, b->filename);
01389 if (diff)
01390 return diff;
01391
01392
01393 return a->number - b->number;
01394 }
01395
01396 static gint tuple_compare (const void * _a, const void * _b, void * _compare)
01397 {
01398 const struct entry * a = _a, * b = _b;
01399 gint (* compare) (const Tuple * a, const Tuple * b) = _compare;
01400
01401 if (a->tuple == NULL)
01402 return (b->tuple == NULL) ? 0 : -1;
01403 if (b->tuple == NULL)
01404 return 1;
01405
01406 gint diff = compare (a->tuple, b->tuple);
01407 if (diff)
01408 return diff;
01409
01410
01411 return a->number - b->number;
01412 }
01413
01414 static void sort (struct playlist * playlist, gint (* compare) (const void * a,
01415 const void * b, void * inner), void * inner)
01416 {
01417 PLAYLIST_WILL_CHANGE;
01418
01419 index_sort_with_data (playlist->entries, compare, inner);
01420 number_entries (playlist, 0, index_count (playlist->entries));
01421
01422 PLAYLIST_HAS_CHANGED;
01423 }
01424
01425 static void sort_selected (struct playlist * playlist, gint (* compare)
01426 (const void * a, const void * b, void * inner), void * inner)
01427 {
01428 gint entries, count, count2;
01429 struct index *selected;
01430
01431 PLAYLIST_WILL_CHANGE;
01432
01433 entries = index_count(playlist->entries);
01434 selected = index_new();
01435
01436 for (count = 0; count < entries; count++)
01437 {
01438 struct entry *entry = index_get(playlist->entries, count);
01439
01440 if (entry->selected)
01441 index_append(selected, entry);
01442 }
01443
01444 index_sort_with_data (selected, compare, inner);
01445
01446 count2 = 0;
01447
01448 for (count = 0; count < entries; count++)
01449 {
01450 struct entry *entry = index_get(playlist->entries, count);
01451
01452 if (entry->selected)
01453 index_set(playlist->entries, count, index_get(selected, count2++));
01454 }
01455
01456 index_free(selected);
01457 number_entries(playlist, 0, entries);
01458
01459 PLAYLIST_HAS_CHANGED;
01460 }
01461
01462 void playlist_sort_by_filename (gint playlist_num, gint (* compare)
01463 (const gchar * a, const gchar * b))
01464 {
01465 DECLARE_PLAYLIST;
01466
01467 LOOKUP_PLAYLIST;
01468
01469 sort (playlist, filename_compare, compare);
01470 }
01471
01472 void playlist_sort_by_tuple (gint playlist_num, gint (* compare)
01473 (const Tuple * a, const Tuple * b))
01474 {
01475 DECLARE_PLAYLIST;
01476 LOOKUP_PLAYLIST;
01477 check_all_scanned (playlist);
01478 sort (playlist, tuple_compare, compare);
01479 }
01480
01481 void playlist_sort_selected_by_filename (gint playlist_num, gint (* compare)
01482 (const gchar * a, const gchar * b))
01483 {
01484 DECLARE_PLAYLIST;
01485
01486 LOOKUP_PLAYLIST;
01487
01488 sort_selected (playlist, filename_compare, compare);
01489 }
01490
01491 void playlist_sort_selected_by_tuple (gint playlist_num, gint (* compare)
01492 (const Tuple * a, const Tuple * b))
01493 {
01494 DECLARE_PLAYLIST;
01495 LOOKUP_PLAYLIST;
01496 check_selected_scanned (playlist);
01497 sort_selected (playlist, tuple_compare, compare);
01498 }
01499
01500 void playlist_reformat_titles (void)
01501 {
01502 gint playlist_num;
01503
01504 METADATA_WILL_CHANGE;
01505
01506 for (playlist_num = 0; playlist_num < index_count(playlists); playlist_num++)
01507 {
01508 struct playlist *playlist = index_get(playlists, playlist_num);
01509 gint entries = index_count(playlist->entries);
01510 gint count;
01511
01512 for (count = 0; count < entries; count++)
01513 {
01514 struct entry *entry = index_get(playlist->entries, count);
01515
01516 g_free(entry->title);
01517 entry->title = (entry->tuple == NULL) ? NULL : title_from_tuple(entry->tuple);
01518 }
01519 }
01520
01521 METADATA_HAS_CHANGED;
01522 }
01523
01524 void playlist_rescan(gint playlist_num)
01525 {
01526 DECLARE_PLAYLIST;
01527 gint entries, count;
01528
01529 LOOKUP_PLAYLIST;
01530 METADATA_WILL_CHANGE;
01531
01532 entries = index_count(playlist->entries);
01533
01534 for (count = 0; count < entries; count++)
01535 {
01536 struct entry *entry = index_get(playlist->entries, count);
01537
01538 entry_set_tuple (playlist, entry, NULL);
01539 entry->failed = FALSE;
01540 }
01541
01542 METADATA_HAS_CHANGED;
01543 }
01544
01545 void playlist_rescan_file (const gchar * filename)
01546 {
01547 gint num_playlists = index_count (playlists);
01548 gint playlist_num;
01549
01550 METADATA_WILL_CHANGE;
01551
01552 gchar * copy = NULL;
01553 if (! uri_is_utf8 (filename, TRUE))
01554 filename = copy = uri_to_utf8 (filename);
01555
01556 for (playlist_num = 0; playlist_num < num_playlists; playlist_num ++)
01557 {
01558 struct playlist * playlist = index_get (playlists, playlist_num);
01559 gint num_entries = index_count (playlist->entries);
01560 gint entry_num;
01561
01562 for (entry_num = 0; entry_num < num_entries; entry_num ++)
01563 {
01564 struct entry * entry = index_get (playlist->entries, entry_num);
01565
01566 if (! strcmp (entry->filename, filename))
01567 {
01568 entry_set_tuple (playlist, entry, NULL);
01569 entry->failed = FALSE;
01570 }
01571 }
01572 }
01573
01574 g_free (copy);
01575
01576 METADATA_HAS_CHANGED;
01577 }
01578
01579 gint64 playlist_get_total_length (gint playlist_num, gboolean fast)
01580 {
01581 DECLARE_PLAYLIST;
01582 LOOKUP_PLAYLIST_RET (0);
01583
01584 if (! fast)
01585 check_all_scanned (playlist);
01586
01587 return playlist->total_length;
01588 }
01589
01590 gint64 playlist_get_selected_length (gint playlist_num, gboolean fast)
01591 {
01592 DECLARE_PLAYLIST;
01593 LOOKUP_PLAYLIST_RET (0);
01594
01595 if (! fast)
01596 check_selected_scanned (playlist);
01597
01598 return playlist->selected_length;
01599 }
01600
01601 gint playlist_queue_count(gint playlist_num)
01602 {
01603 DECLARE_PLAYLIST;
01604
01605 LOOKUP_PLAYLIST_RET (0);
01606
01607 return g_list_length (playlist->queued);
01608 }
01609
01610 void playlist_queue_insert(gint playlist_num, gint at, gint entry_num)
01611 {
01612 DECLARE_PLAYLIST_ENTRY;
01613
01614 LOOKUP_PLAYLIST_ENTRY;
01615
01616 if (entry->queued)
01617 return;
01618
01619 if (at < 0)
01620 playlist->queued = g_list_append(playlist->queued, entry);
01621 else
01622 playlist->queued = g_list_insert(playlist->queued, entry, at);
01623
01624 entry->queued = TRUE;
01625
01626 SELECTION_HAS_CHANGED;
01627 }
01628
01629 void playlist_queue_insert_selected (gint playlist_num, gint at)
01630 {
01631 DECLARE_PLAYLIST;
01632 gint entries, count;
01633
01634 LOOKUP_PLAYLIST;
01635 entries = index_count(playlist->entries);
01636
01637 for (count = 0; count < entries; count++)
01638 {
01639 struct entry *entry = index_get(playlist->entries, count);
01640
01641 if (!entry->selected || entry->queued)
01642 continue;
01643
01644 if (at < 0)
01645 playlist->queued = g_list_append(playlist->queued, entry);
01646 else
01647 playlist->queued = g_list_insert(playlist->queued, entry, at++);
01648
01649 entry->queued = TRUE;
01650 }
01651
01652 SELECTION_HAS_CHANGED;
01653 }
01654
01655 gint playlist_queue_get_entry(gint playlist_num, gint at)
01656 {
01657 DECLARE_PLAYLIST;
01658 GList *node;
01659 struct entry *entry;
01660
01661 LOOKUP_PLAYLIST_RET (-1);
01662 node = g_list_nth(playlist->queued, at);
01663
01664 if (node == NULL)
01665 return -1;
01666
01667 entry = node->data;
01668 return entry->number;
01669 }
01670
01671 gint playlist_queue_find_entry(gint playlist_num, gint entry_num)
01672 {
01673 DECLARE_PLAYLIST_ENTRY;
01674
01675 LOOKUP_PLAYLIST_ENTRY_RET (-1);
01676
01677 if (! entry->queued)
01678 return -1;
01679
01680 return g_list_index(playlist->queued, entry);
01681 }
01682
01683 void playlist_queue_delete(gint playlist_num, gint at, gint number)
01684 {
01685 DECLARE_PLAYLIST;
01686
01687 LOOKUP_PLAYLIST;
01688
01689 if (at == 0)
01690 {
01691 while (playlist->queued != NULL && number--)
01692 {
01693 struct entry *entry = playlist->queued->data;
01694
01695 playlist->queued = g_list_delete_link(playlist->queued, playlist->queued);
01696
01697 entry->queued = FALSE;
01698 }
01699 }
01700 else
01701 {
01702 GList *anchor = g_list_nth(playlist->queued, at - 1);
01703
01704 if (anchor == NULL)
01705 goto DONE;
01706
01707 while (anchor->next != NULL && number--)
01708 {
01709 struct entry *entry = anchor->next->data;
01710
01711 playlist->queued = g_list_delete_link(playlist->queued, anchor->next);
01712
01713 entry->queued = FALSE;
01714 }
01715 }
01716
01717 DONE:
01718 SELECTION_HAS_CHANGED;
01719 }
01720
01721 void playlist_queue_delete_selected (gint playlist_num)
01722 {
01723 DECLARE_PLAYLIST;
01724 LOOKUP_PLAYLIST;
01725
01726 for (GList * node = playlist->queued; node != NULL; )
01727 {
01728 GList * next = node->next;
01729 struct entry * entry = node->data;
01730 if (entry->selected)
01731 playlist->queued = g_list_delete_link (playlist->queued, node);
01732 node = next;
01733 }
01734
01735 SELECTION_HAS_CHANGED;
01736 }
01737
01738 static gboolean shuffle_prev (struct playlist * playlist)
01739 {
01740 gint entries = index_count (playlist->entries), count;
01741 struct entry * found = NULL;
01742
01743 for (count = 0; count < entries; count ++)
01744 {
01745 struct entry * entry = index_get (playlist->entries, count);
01746
01747 if (entry->shuffle_num && (playlist->position == NULL ||
01748 entry->shuffle_num < playlist->position->shuffle_num) && (found == NULL
01749 || entry->shuffle_num > found->shuffle_num))
01750 found = entry;
01751 }
01752
01753 if (found == NULL)
01754 return FALSE;
01755
01756 playlist->position = found;
01757 return TRUE;
01758 }
01759
01760 gboolean playlist_prev_song(gint playlist_num)
01761 {
01762 DECLARE_PLAYLIST;
01763
01764 LOOKUP_PLAYLIST_RET (FALSE);
01765
01766 if (cfg.shuffle)
01767 {
01768 if (! shuffle_prev (playlist))
01769 return FALSE;
01770 }
01771 else
01772 {
01773 if (playlist->position == NULL || playlist->position->number == 0)
01774 return FALSE;
01775
01776 set_position (playlist, index_get (playlist->entries,
01777 playlist->position->number - 1));
01778 }
01779
01780 if (playlist == playing_playlist && playback_get_playing ())
01781 playback_stop();
01782
01783 SELECTION_HAS_CHANGED;
01784
01785 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
01786 return TRUE;
01787 }
01788
01789 static gboolean shuffle_next (struct playlist * playlist)
01790 {
01791 gint entries = index_count (playlist->entries), choice = 0, count;
01792 struct entry * found = NULL;
01793
01794 for (count = 0; count < entries; count ++)
01795 {
01796 struct entry * entry = index_get (playlist->entries, count);
01797
01798 if (! entry->shuffle_num)
01799 choice ++;
01800 else if (playlist->position != NULL && entry->shuffle_num >
01801 playlist->position->shuffle_num && (found == NULL || entry->shuffle_num
01802 < found->shuffle_num))
01803 found = entry;
01804 }
01805
01806 if (found != NULL)
01807 {
01808 playlist->position = found;
01809 return TRUE;
01810 }
01811
01812 if (! choice)
01813 return FALSE;
01814
01815 choice = random () % choice;
01816
01817 for (count = 0; ; count ++)
01818 {
01819 struct entry * entry = index_get (playlist->entries, count);
01820
01821 if (! entry->shuffle_num)
01822 {
01823 if (! choice)
01824 {
01825 set_position (playlist, entry);
01826 return TRUE;
01827 }
01828
01829 choice --;
01830 }
01831 }
01832 }
01833
01834 static void shuffle_reset (struct playlist * playlist)
01835 {
01836 gint entries = index_count (playlist->entries), count;
01837
01838 playlist->last_shuffle_num = 0;
01839
01840 for (count = 0; count < entries; count ++)
01841 {
01842 struct entry * entry = index_get (playlist->entries, count);
01843
01844 entry->shuffle_num = 0;
01845 }
01846 }
01847
01848 gboolean playlist_next_song(gint playlist_num, gboolean repeat)
01849 {
01850 DECLARE_PLAYLIST;
01851 gint entries;
01852
01853 LOOKUP_PLAYLIST_RET (FALSE);
01854 entries = index_count(playlist->entries);
01855
01856 if (entries == 0)
01857 return FALSE;
01858
01859
01860 if (playlist->queued != NULL)
01861 {
01862 set_position (playlist, playlist->queued->data);
01863
01864 playlist->queued = g_list_remove(playlist->queued, playlist->position);
01865 playlist->position->queued = FALSE;
01866 }
01867 else if (cfg.shuffle)
01868 {
01869 if (! shuffle_next (playlist))
01870 {
01871 if (! repeat)
01872 return FALSE;
01873
01874 shuffle_reset (playlist);
01875
01876 if (! shuffle_next (playlist))
01877 return FALSE;
01878 }
01879 }
01880 else
01881 {
01882 if (playlist->position == NULL)
01883 set_position (playlist, index_get (playlist->entries, 0));
01884 else if (playlist->position->number == entries - 1)
01885 {
01886 if (!repeat)
01887 return FALSE;
01888
01889 set_position (playlist, index_get (playlist->entries, 0));
01890 }
01891 else
01892 set_position (playlist, index_get (playlist->entries,
01893 playlist->position->number + 1));
01894 }
01895
01896 if (playlist == playing_playlist && playback_get_playing ())
01897 playback_stop();
01898
01899 SELECTION_HAS_CHANGED;
01900
01901 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
01902 return TRUE;
01903 }
01904
01905 gboolean playlist_update_pending (void)
01906 {
01907 return (update_source != 0);
01908 }
01909
01910 void playlist_save_state (void)
01911 {
01912 gchar scratch[512];
01913 FILE * handle;
01914 gint playlist_num;
01915
01916 snprintf (scratch, sizeof scratch, "%s/" STATE_FILE,
01917 aud_paths[BMP_PATH_USER_DIR]);
01918 handle = fopen (scratch, "w");
01919
01920 if (handle == NULL)
01921 return;
01922
01923 fprintf (handle, "active %d\n", playlist_get_active ());
01924 fprintf (handle, "playing %d\n", playlist_get_playing ());
01925
01926 for (playlist_num = 0; playlist_num < index_count (playlists);
01927 playlist_num ++)
01928 {
01929 struct playlist * playlist = index_get (playlists, playlist_num);
01930 gint entries = index_count (playlist->entries), count;
01931
01932 fprintf (handle, "playlist %d\n", playlist_num);
01933 fprintf (handle, "position %d\n", playlist_get_position (playlist_num));
01934 fprintf (handle, "last-shuffled %d\n", playlist->last_shuffle_num);
01935
01936 for (count = 0; count < entries; count ++)
01937 {
01938 struct entry * entry = index_get (playlist->entries, count);
01939
01940 fprintf (handle, "S %d\n", entry->shuffle_num);
01941 }
01942 }
01943
01944 fclose (handle);
01945 }
01946
01947 static gchar parse_key[32];
01948 static gchar * parse_value;
01949
01950 static void parse_next (FILE * handle)
01951 {
01952 gchar * found;
01953
01954 parse_value = NULL;
01955
01956 if (fgets (parse_key, sizeof parse_key, handle) == NULL)
01957 return;
01958
01959 found = strchr (parse_key, ' ');
01960
01961 if (found == NULL)
01962 return;
01963
01964 * found = 0;
01965 parse_value = found + 1;
01966
01967 found = strchr (parse_value, '\n');
01968
01969 if (found != NULL)
01970 * found = 0;
01971 }
01972
01973 static gboolean parse_integer (const gchar * key, gint * value)
01974 {
01975 return (parse_value != NULL && ! strcmp (parse_key, key) && sscanf
01976 (parse_value, "%d", value) == 1);
01977 }
01978
01979 void playlist_load_state (void)
01980 {
01981 gchar scratch[512];
01982 FILE * handle;
01983 gint playlist_num, obsolete = 0;
01984
01985 snprintf (scratch, sizeof scratch, "%s/" STATE_FILE,
01986 aud_paths[BMP_PATH_USER_DIR]);
01987 handle = fopen (scratch, "r");
01988
01989 if (handle == NULL)
01990 return;
01991
01992 parse_next (handle);
01993
01994 if (parse_integer ("active", & playlist_num))
01995 {
01996 playlist_set_active (playlist_num);
01997 parse_next (handle);
01998 }
01999
02000 if (parse_integer ("playing", & playlist_num))
02001 {
02002 playlist_set_playing (playlist_num);
02003 parse_next (handle);
02004 }
02005
02006 while (parse_integer ("playlist", & playlist_num) && playlist_num >= 0 &&
02007 playlist_num < index_count (playlists))
02008 {
02009 struct playlist * playlist = index_get (playlists, playlist_num);
02010 gint entries = index_count (playlist->entries), position, count;
02011
02012 parse_next (handle);
02013
02014 if (parse_integer ("position", & position))
02015 parse_next (handle);
02016
02017 if (position >= 0 && position < entries)
02018 playlist->position = index_get (playlist->entries, position);
02019
02020 if (parse_integer ("shuffled", & obsolete))
02021 parse_next (handle);
02022
02023 if (parse_integer ("last-shuffled", & playlist->last_shuffle_num))
02024 parse_next (handle);
02025 else
02026 playlist->last_shuffle_num = obsolete;
02027
02028 for (count = 0; count < entries; count ++)
02029 {
02030 struct entry * entry = index_get (playlist->entries, count);
02031
02032 if (parse_integer ("S", & entry->shuffle_num))
02033 parse_next (handle);
02034 }
02035 }
02036
02037 fclose (handle);
02038 }