• Main Page
  • Related Pages
  • Classes
  • Files
  • File List
  • File Members

playlist-new.c

Go to the documentation of this file.
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 }

Generated on Wed Apr 6 2011 for Audacious by  doxygen 1.7.1