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