Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * playlist-new.c 00003 * Copyright 2009-2010 John Lindgren 00004 * 00005 * This file is part of Audacious. 00006 * 00007 * Audacious is free software: you can redistribute it and/or modify it under 00008 * the terms of the GNU General Public License as published by the Free Software 00009 * Foundation, version 2 or version 3 of the License. 00010 * 00011 * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY 00012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 00013 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along with 00016 * Audacious. If not, see <http://www.gnu.org/licenses/>. 00017 * 00018 * The Audacious team does not consider modular code linking to Audacious or 00019 * using our public API to be a derived work. 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 /* Hack: We cannot refresh segmented entries (since their info is read from 00172 * the cue sheet when it is first loaded), so leave them alone. -jlindgren */ 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 /* scan_mutex must be locked! */ 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; /* thread not in use or still working */ 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; /* thread already in use */ 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]); /* scan in progress == very, very bad */ 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 /* As soon as we know the caller is looking for metadata, we start the threaded 00540 * scanner. Though it may be faster in the short run simply to scan the entry 00541 * we are concerned with in the main thread, this is better in the long run 00542 * because the scanner can work on the following entries while the caller is 00543 * processing this one. */ 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 /* If we are already at the song or it is already at the top of the shuffle 00855 * list, we let it be. Otherwise, we move it to the top. */ 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 /* preserve order of "equal" entries */ 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 /* preserve order of "equal" entries */ 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 /* If we have a song in queue, jump to it, _then_ remove it from queue */ 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)) /* compatibility with 2.3 */ 02021 parse_next (handle); 02022 02023 if (parse_integer ("last-shuffled", & playlist->last_shuffle_num)) 02024 parse_next (handle); 02025 else /* compatibility with 2.3 beta */ 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 }