• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/xmms/playlist.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 
00018 /** @file
00019  *  Controls playlist
00020  */
00021 
00022 #include <stdio.h>
00023 #include <unistd.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <glib.h>
00027 #include <math.h>
00028 #include <ctype.h>
00029 
00030 #include "xmmspriv/xmms_playlist.h"
00031 #include "xmms/xmms_ipc.h"
00032 #include "xmms/xmms_config.h"
00033 #include "xmmspriv/xmms_medialib.h"
00034 #include "xmmspriv/xmms_collection.h"
00035 #include "xmms/xmms_log.h"
00036 /*
00037 #include "xmms/plsplugins.h"
00038 #include "xmms/util.h"
00039 #include "xmms/signal_xmms.h"
00040 #include "xmms/ipc.h"
00041 #include "xmms/mediainfo.h"
00042 #include "xmms/magic.h"
00043 */
00044 static void xmms_playlist_destroy (xmms_object_t *object);
00045 static void xmms_playlist_shuffle (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
00046 static void xmms_playlist_clear (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
00047 static void xmms_playlist_sort (xmms_playlist_t *playlist, const gchar *plname, xmmsv_t *property, xmms_error_t *err);
00048 static GList * xmms_playlist_list_entries (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
00049 static gchar *xmms_playlist_current_active (xmms_playlist_t *playlist, xmms_error_t *err);
00050 static void xmms_playlist_destroy (xmms_object_t *object);
00051 gboolean xmms_playlist_remove (xmms_playlist_t *playlist, const gchar *plname, guint pos, xmms_error_t *err);
00052 static gboolean xmms_playlist_remove_unlocked (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *plcoll, guint pos, xmms_error_t *err);
00053 static gboolean xmms_playlist_move (xmms_playlist_t *playlist, const gchar *plname, guint pos, guint newpos, xmms_error_t *err);
00054 static gint xmms_playlist_set_current_position_rel (xmms_playlist_t *playlist, gint32 pos, xmms_error_t *error);
00055 static gint xmms_playlist_set_current_position_do (xmms_playlist_t *playlist, guint32 pos, xmms_error_t *err);
00056 
00057 static gboolean xmms_playlist_insert_url (xmms_playlist_t *playlist, const gchar *plname, guint32 pos, const gchar *url, xmms_error_t *error);
00058 static gboolean xmms_playlist_insert_id (xmms_playlist_t *playlist, const gchar *plname, guint32 pos, xmms_medialib_entry_t file, xmms_error_t *error);
00059 static gboolean xmms_playlist_insert_collection (xmms_playlist_t *playlist, const gchar *plname, guint32 pos, xmmsv_coll_t *coll, xmmsv_t *order, xmms_error_t *error);
00060 static void xmms_playlist_radd (xmms_playlist_t *playlist, const gchar *plname, const gchar *path, xmms_error_t *error);
00061 static void xmms_playlist_rinsert (xmms_playlist_t *playlist, const gchar *plname, guint32 pos, const gchar *path, xmms_error_t *error);
00062 
00063 static void xmms_playlist_load (xmms_playlist_t *, const gchar *, xmms_error_t *);
00064 
00065 static xmmsv_coll_t *xmms_playlist_get_coll (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *error);
00066 static const gchar *xmms_playlist_canonical_name (xmms_playlist_t *playlist, const gchar *plname);
00067 static gint xmms_playlist_coll_get_currpos (xmmsv_coll_t *plcoll);
00068 static gint xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll);
00069 
00070 static void xmms_playlist_update_queue (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll);
00071 static void xmms_playlist_update_partyshuffle (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll);
00072 
00073 static void xmms_playlist_current_pos_msg_send (xmms_playlist_t *playlist, GTree *dict);
00074 static GTree * xmms_playlist_current_pos_msg_new (xmms_playlist_t *playlist, guint32 pos, const gchar *plname);
00075 
00076 XMMS_CMD_DEFINE  (load, xmms_playlist_load, xmms_playlist_t *, NONE, STRING, NONE);
00077 XMMS_CMD_DEFINE3 (insert_url, xmms_playlist_insert_url, xmms_playlist_t *, NONE, STRING, INT32, STRING);
00078 XMMS_CMD_DEFINE3 (insert_id, xmms_playlist_insert_id, xmms_playlist_t *, NONE, STRING, INT32, INT32);
00079 XMMS_CMD_DEFINE4 (insert_coll, xmms_playlist_insert_collection, xmms_playlist_t *, NONE, STRING, INT32, COLL, LIST);
00080 XMMS_CMD_DEFINE  (shuffle, xmms_playlist_shuffle, xmms_playlist_t *, NONE, STRING, NONE);
00081 XMMS_CMD_DEFINE  (remove, xmms_playlist_remove, xmms_playlist_t *, NONE, STRING, INT32);
00082 XMMS_CMD_DEFINE3 (move, xmms_playlist_move, xmms_playlist_t *, NONE, STRING, INT32, INT32);
00083 XMMS_CMD_DEFINE  (add_url, xmms_playlist_add_url, xmms_playlist_t *, NONE, STRING, STRING);
00084 XMMS_CMD_DEFINE  (add_id, xmms_playlist_add_id, xmms_playlist_t *, NONE, STRING, INT32);
00085 XMMS_CMD_DEFINE  (add_idlist, xmms_playlist_add_idlist, xmms_playlist_t *, NONE, STRING, COLL);
00086 XMMS_CMD_DEFINE3 (add_coll, xmms_playlist_add_collection, xmms_playlist_t *, NONE, STRING, COLL, LIST);
00087 XMMS_CMD_DEFINE  (clear, xmms_playlist_clear, xmms_playlist_t *, NONE, STRING, NONE);
00088 XMMS_CMD_DEFINE  (sort, xmms_playlist_sort, xmms_playlist_t *, NONE, STRING, LIST);
00089 XMMS_CMD_DEFINE  (list_entries, xmms_playlist_list_entries, xmms_playlist_t *, LIST, STRING, NONE);
00090 XMMS_CMD_DEFINE  (current_pos, xmms_playlist_current_pos, xmms_playlist_t *, DICT, STRING, NONE);
00091 XMMS_CMD_DEFINE  (current_active, xmms_playlist_current_active, xmms_playlist_t *, STRING, NONE, NONE);
00092 XMMS_CMD_DEFINE  (set_pos, xmms_playlist_set_current_position, xmms_playlist_t *, INT32, INT32, NONE);
00093 XMMS_CMD_DEFINE  (set_pos_rel, xmms_playlist_set_current_position_rel, xmms_playlist_t *, INT32, INT32, NONE);
00094 XMMS_CMD_DEFINE  (radd, xmms_playlist_radd, xmms_playlist_t *, NONE, STRING, STRING);
00095 XMMS_CMD_DEFINE3 (rinsert, xmms_playlist_rinsert, xmms_playlist_t *, NONE, STRING, INT32, STRING);
00096 
00097 #define XMMS_PLAYLIST_CHANGED_MSG(type, id, name) xmms_playlist_changed_msg_send (playlist, xmms_playlist_changed_msg_new (playlist, type, id, name))
00098 #define XMMS_PLAYLIST_CURRPOS_MSG(pos, name) xmms_playlist_current_pos_msg_send (playlist, xmms_playlist_current_pos_msg_new (playlist, pos, name))
00099 
00100 
00101 /** @defgroup Playlist Playlist
00102   * @ingroup XMMSServer
00103   * @brief This is the playlist control.
00104   *
00105   * A playlist is a central thing in the XMMS server, it
00106   * tells us what to do after we played the following entry
00107   * @{
00108   */
00109 
00110 /** Playlist structure */
00111 struct xmms_playlist_St {
00112     xmms_object_t object;
00113 
00114     /* playlists are in the collection DAG */
00115     xmms_coll_dag_t *colldag;
00116 
00117     gboolean repeat_one;
00118     gboolean repeat_all;
00119 
00120     GMutex *mutex;
00121 
00122     xmms_mediainfo_reader_t *mediainfordr;
00123 
00124     gboolean update_flag;
00125     xmms_medialib_t *medialib;
00126 };
00127 
00128 
00129 static void
00130 on_playlist_r_all_changed (xmms_object_t *object, xmmsv_t *_data,
00131                            gpointer udata)
00132 {
00133     xmms_playlist_t *playlist = udata;
00134     gint value;
00135 
00136     value = xmms_config_property_get_int ((xmms_config_property_t *) object);
00137 
00138     g_mutex_lock (playlist->mutex);
00139     playlist->repeat_all = !!value;
00140     g_mutex_unlock (playlist->mutex);
00141 }
00142 
00143 static void
00144 on_playlist_r_one_changed (xmms_object_t *object, xmmsv_t *_data,
00145                            gpointer udata)
00146 {
00147     xmms_playlist_t *playlist = udata;
00148     gint value;
00149 
00150     value = xmms_config_property_get_int ((xmms_config_property_t *) object);
00151 
00152     g_mutex_lock (playlist->mutex);
00153     playlist->repeat_one = !!value;
00154     g_mutex_unlock (playlist->mutex);
00155 }
00156 
00157 
00158 static void
00159 on_playlist_updated (xmms_object_t *object, const gchar *plname)
00160 {
00161     xmmsv_coll_t *plcoll;
00162     xmms_playlist_t *playlist = (xmms_playlist_t*)object;
00163 
00164     /* Already in an update process, quit */
00165     if (playlist->update_flag) {
00166         return;
00167     }
00168 
00169     plcoll = xmms_playlist_get_coll (playlist, plname, NULL);
00170     if (plcoll == NULL) {
00171         return;
00172     } else {
00173         /* Run the update function if appropriate */
00174         switch (xmmsv_coll_get_type (plcoll)) {
00175         case XMMS_COLLECTION_TYPE_QUEUE:
00176             xmms_playlist_update_queue (playlist, plname, plcoll);
00177             break;
00178 
00179         case XMMS_COLLECTION_TYPE_PARTYSHUFFLE:
00180             xmms_playlist_update_partyshuffle (playlist, plname, plcoll);
00181             break;
00182 
00183         default:
00184             break;
00185         }
00186     }
00187 }
00188 
00189 static void
00190 on_playlist_updated_pos (xmms_object_t *object, xmmsv_t *val, gpointer udata)
00191 {
00192     XMMS_DBG ("PLAYLIST: updated pos!");
00193     on_playlist_updated (object, XMMS_ACTIVE_PLAYLIST);
00194 }
00195 
00196 static void
00197 on_playlist_updated_chg (xmms_object_t *object, xmmsv_t *val, gpointer udata)
00198 {
00199     const gchar *plname = NULL;
00200     xmmsv_t *pl_val;
00201 
00202     XMMS_DBG ("PLAYLIST: updated chg!");
00203 
00204     xmmsv_dict_get (val, "name", &pl_val);
00205     if (pl_val != NULL) {
00206         xmmsv_get_string (pl_val, &plname);
00207     } else {
00208         /* FIXME: occurs? */
00209         XMMS_DBG ("PLAYLIST: updated_chg, NULL playlist!");
00210         g_assert_not_reached ();
00211     }
00212 
00213     on_playlist_updated (object, plname);
00214 }
00215 
00216 static void
00217 xmms_playlist_update_queue (xmms_playlist_t *playlist, const gchar *plname,
00218                             xmmsv_coll_t *coll)
00219 {
00220     gint history, currpos;
00221 
00222     XMMS_DBG ("PLAYLIST: update-queue!");
00223 
00224     if (!xmms_collection_get_int_attr (coll, "history", &history)) {
00225         history = 0;
00226     }
00227 
00228     playlist->update_flag = TRUE;
00229     currpos = xmms_playlist_coll_get_currpos (coll);
00230     while (currpos > history) {
00231         xmms_playlist_remove_unlocked (playlist, plname, coll, 0, NULL);
00232         currpos = xmms_playlist_coll_get_currpos (coll);
00233     }
00234     playlist->update_flag = FALSE;
00235 }
00236 
00237 static void
00238 xmms_playlist_update_partyshuffle (xmms_playlist_t *playlist,
00239                                    const gchar *plname, xmmsv_coll_t *coll)
00240 {
00241     gint history, upcoming, currpos, size;
00242     xmmsv_coll_t *src;
00243 
00244     XMMS_DBG ("PLAYLIST: update-partyshuffle!");
00245 
00246     if (!xmms_collection_get_int_attr (coll, "history", &history)) {
00247         history = 0;
00248     }
00249 
00250     if (!xmms_collection_get_int_attr (coll, "upcoming", &upcoming)) {
00251         upcoming = XMMS_DEFAULT_PARTYSHUFFLE_UPCOMING;
00252     }
00253 
00254     playlist->update_flag = TRUE;
00255     currpos = xmms_playlist_coll_get_currpos (coll);
00256     while (currpos > history) {
00257         xmms_playlist_remove_unlocked (playlist, plname, coll, 0, NULL);
00258         currpos = xmms_playlist_coll_get_currpos (coll);
00259     }
00260 
00261     xmmsv_coll_operand_list_save (coll);
00262     xmmsv_coll_operand_list_first (coll);
00263     if (!xmmsv_coll_operand_list_entry (coll, &src)) {
00264         XMMS_DBG ("Cannot find party shuffle operand!");
00265         return;
00266     }
00267     xmmsv_coll_operand_list_restore (coll);
00268 
00269     currpos = xmms_playlist_coll_get_currpos (coll);
00270     size = xmms_playlist_coll_get_size (coll);
00271     while (size < currpos + 1 + upcoming) {
00272         xmms_medialib_entry_t randentry;
00273         randentry = xmms_collection_get_random_media (playlist->colldag, src);
00274         if (randentry == 0) {
00275             break;  /* No media found in the collection, give up */
00276         }
00277         /* FIXME: add_collection might yield better perf here. */
00278         xmms_playlist_add_entry_unlocked (playlist, plname, coll, randentry, NULL);
00279 
00280         currpos = xmms_playlist_coll_get_currpos (coll);
00281         size = xmms_playlist_coll_get_size (coll);
00282     }
00283     playlist->update_flag = FALSE;
00284 }
00285 
00286 
00287 /**
00288  * Initializes a new xmms_playlist_t.
00289  */
00290 xmms_playlist_t *
00291 xmms_playlist_init (void)
00292 {
00293     xmms_playlist_t *ret;
00294     xmms_config_property_t *val;
00295 
00296     ret = xmms_object_new (xmms_playlist_t, xmms_playlist_destroy);
00297     ret->mutex = g_mutex_new ();
00298 
00299     xmms_ipc_object_register (XMMS_IPC_OBJECT_PLAYLIST, XMMS_OBJECT (ret));
00300 
00301     xmms_ipc_broadcast_register (XMMS_OBJECT (ret),
00302                                  XMMS_IPC_SIGNAL_PLAYLIST_CHANGED);
00303     xmms_ipc_broadcast_register (XMMS_OBJECT (ret),
00304                                  XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS);
00305     xmms_ipc_broadcast_register (XMMS_OBJECT (ret),
00306                                  XMMS_IPC_SIGNAL_PLAYLIST_LOADED);
00307 
00308     val = xmms_config_property_register ("playlist.repeat_one", "0",
00309                                          on_playlist_r_one_changed, ret);
00310     ret->repeat_one = xmms_config_property_get_int (val);
00311 
00312     val = xmms_config_property_register ("playlist.repeat_all", "0",
00313                                       on_playlist_r_all_changed, ret);
00314     ret->repeat_all = xmms_config_property_get_int (val);
00315 
00316     ret->update_flag = FALSE;
00317 
00318     xmms_object_connect (XMMS_OBJECT (ret),
00319                          XMMS_IPC_SIGNAL_PLAYLIST_CHANGED,
00320                          on_playlist_updated_chg, ret);
00321 
00322     xmms_object_connect (XMMS_OBJECT (ret),
00323                          XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS,
00324                          on_playlist_updated_pos, ret);
00325 
00326 
00327     xmms_object_cmd_add (XMMS_OBJECT (ret),
00328                          XMMS_IPC_CMD_CURRENT_POS,
00329                          XMMS_CMD_FUNC (current_pos));
00330 
00331     xmms_object_cmd_add (XMMS_OBJECT (ret),
00332                          XMMS_IPC_CMD_CURRENT_ACTIVE,
00333                          XMMS_CMD_FUNC (current_active));
00334 
00335     xmms_object_cmd_add (XMMS_OBJECT (ret),
00336                          XMMS_IPC_CMD_LOAD,
00337                          XMMS_CMD_FUNC (load));
00338 
00339     xmms_object_cmd_add (XMMS_OBJECT (ret),
00340                          XMMS_IPC_CMD_SHUFFLE,
00341                          XMMS_CMD_FUNC (shuffle));
00342 
00343     xmms_object_cmd_add (XMMS_OBJECT (ret),
00344                          XMMS_IPC_CMD_SET_POS,
00345                          XMMS_CMD_FUNC (set_pos));
00346 
00347     xmms_object_cmd_add (XMMS_OBJECT (ret),
00348                          XMMS_IPC_CMD_SET_POS_REL,
00349                          XMMS_CMD_FUNC (set_pos_rel));
00350 
00351     xmms_object_cmd_add (XMMS_OBJECT (ret),
00352                          XMMS_IPC_CMD_ADD_URL,
00353                          XMMS_CMD_FUNC (add_url));
00354 
00355     xmms_object_cmd_add (XMMS_OBJECT (ret),
00356                          XMMS_IPC_CMD_ADD_ID,
00357                          XMMS_CMD_FUNC (add_id));
00358 
00359     xmms_object_cmd_add (XMMS_OBJECT (ret),
00360                          XMMS_IPC_CMD_ADD_IDLIST,
00361                          XMMS_CMD_FUNC (add_idlist));
00362 
00363     xmms_object_cmd_add (XMMS_OBJECT (ret),
00364                          XMMS_IPC_CMD_ADD_COLL,
00365                          XMMS_CMD_FUNC (add_coll));
00366 
00367     xmms_object_cmd_add (XMMS_OBJECT (ret),
00368                          XMMS_IPC_CMD_REMOVE_ENTRY,
00369                          XMMS_CMD_FUNC (remove));
00370 
00371     xmms_object_cmd_add (XMMS_OBJECT (ret),
00372                          XMMS_IPC_CMD_MOVE_ENTRY,
00373                          XMMS_CMD_FUNC (move));
00374 
00375     xmms_object_cmd_add (XMMS_OBJECT (ret),
00376                          XMMS_IPC_CMD_CLEAR,
00377                          XMMS_CMD_FUNC (clear));
00378 
00379     xmms_object_cmd_add (XMMS_OBJECT (ret),
00380                          XMMS_IPC_CMD_SORT,
00381                          XMMS_CMD_FUNC (sort));
00382 
00383     xmms_object_cmd_add (XMMS_OBJECT (ret),
00384                          XMMS_IPC_CMD_LIST,
00385                          XMMS_CMD_FUNC (list_entries));
00386 
00387     xmms_object_cmd_add (XMMS_OBJECT (ret),
00388                          XMMS_IPC_CMD_INSERT_URL,
00389                          XMMS_CMD_FUNC (insert_url));
00390 
00391     xmms_object_cmd_add (XMMS_OBJECT (ret),
00392                          XMMS_IPC_CMD_INSERT_ID,
00393                          XMMS_CMD_FUNC (insert_id));
00394 
00395     xmms_object_cmd_add (XMMS_OBJECT (ret),
00396                          XMMS_IPC_CMD_INSERT_COLL,
00397                          XMMS_CMD_FUNC (insert_coll));
00398 
00399     xmms_object_cmd_add (XMMS_OBJECT (ret),
00400                          XMMS_IPC_CMD_RADD,
00401                          XMMS_CMD_FUNC (radd));
00402 
00403     xmms_object_cmd_add (XMMS_OBJECT (ret),
00404                          XMMS_IPC_CMD_RINSERT,
00405                          XMMS_CMD_FUNC (rinsert));
00406 
00407     ret->medialib = xmms_medialib_init (ret);
00408     ret->mediainfordr = xmms_mediainfo_reader_start ();
00409     ret->colldag = xmms_collection_init (ret);
00410 
00411     return ret;
00412 }
00413 
00414 static gboolean
00415 xmms_playlist_advance_do (xmms_playlist_t *playlist)
00416 {
00417     gint size, currpos;
00418     gboolean ret = TRUE;
00419     xmmsv_coll_t *plcoll;
00420     char *jumplist;
00421     xmms_error_t err;
00422     xmms_playlist_t *buffer = playlist;
00423     guint newpos;
00424 
00425     xmms_error_reset (&err);
00426 
00427     plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, NULL);
00428     if (plcoll == NULL) {
00429         ret = FALSE;
00430     } else if ((size = xmms_playlist_coll_get_size (plcoll)) == 0) {
00431         if (xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) {
00432             xmms_playlist_load (buffer, jumplist, &err);
00433             if (xmms_error_isok (&err)) {
00434                 ret = xmms_playlist_advance_do (playlist);
00435             } else {
00436                 ret = FALSE;
00437             }
00438         } else {
00439             ret = FALSE;
00440         }
00441     } else if (!playlist->repeat_one) {
00442         currpos = xmms_playlist_coll_get_currpos (plcoll);
00443         currpos++;
00444 
00445         if (currpos == size && !playlist->repeat_all &&
00446             xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) {
00447 
00448             xmms_collection_set_int_attr (plcoll, "position", 0);
00449             XMMS_PLAYLIST_CURRPOS_MSG (0, XMMS_ACTIVE_PLAYLIST);
00450 
00451             xmms_playlist_load (buffer, jumplist, &err);
00452             if (xmms_error_isok (&err)) {
00453                 ret = xmms_playlist_advance_do (playlist);
00454             } else {
00455                 ret = FALSE;
00456             }
00457         } else {
00458             newpos = currpos%size;
00459             xmms_collection_set_int_attr (plcoll, "position", newpos);
00460             XMMS_PLAYLIST_CURRPOS_MSG (newpos, XMMS_ACTIVE_PLAYLIST);
00461             ret = (currpos != size) || playlist->repeat_all;
00462         }
00463     }
00464 
00465     return ret;
00466 }
00467 
00468 /**
00469  * Go to next song in playlist according to current playlist mode.
00470  * xmms_playlist_current_entry is to be used to retrieve the entry.
00471  *
00472  * @sa xmms_playlist_current_entry
00473  *
00474  * @returns FALSE if end of playlist is reached, TRUE otherwise.
00475  */
00476 gboolean
00477 xmms_playlist_advance (xmms_playlist_t *playlist)
00478 {
00479     gboolean ret;
00480 
00481     g_return_val_if_fail (playlist, FALSE);
00482 
00483     g_mutex_lock (playlist->mutex);
00484     ret = xmms_playlist_advance_do (playlist);
00485     g_mutex_unlock (playlist->mutex);
00486 
00487     return ret;
00488 }
00489 
00490 /**
00491  * Retrieve the currently active xmms_medialib_entry_t.
00492  *
00493  */
00494 xmms_medialib_entry_t
00495 xmms_playlist_current_entry (xmms_playlist_t *playlist)
00496 {
00497     gint size, currpos;
00498     xmmsv_coll_t *plcoll;
00499     xmms_medialib_entry_t ent = 0;
00500 
00501     g_return_val_if_fail (playlist, 0);
00502 
00503     g_mutex_lock (playlist->mutex);
00504 
00505     plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, NULL);
00506     if (plcoll == NULL) {
00507         /* FIXME: What happens? */
00508         g_mutex_unlock (playlist->mutex);
00509         return 0;
00510     }
00511 
00512     currpos = xmms_playlist_coll_get_currpos (plcoll);
00513     size = xmms_playlist_coll_get_size (plcoll);
00514 
00515     if (currpos == -1 && (size > 0)) {
00516         currpos = 0;
00517         xmms_collection_set_int_attr (plcoll, "position", currpos);
00518         XMMS_PLAYLIST_CURRPOS_MSG (0, XMMS_ACTIVE_PLAYLIST);
00519     }
00520 
00521     if (currpos < size) {
00522         guint *idlist;
00523         idlist = xmmsv_coll_get_idlist (plcoll);
00524         ent = idlist[currpos];
00525     } else {
00526         ent = 0;
00527     }
00528 
00529     g_mutex_unlock (playlist->mutex);
00530 
00531     return ent;
00532 }
00533 
00534 
00535 /**
00536  * Retrieve the position of the currently active xmms_medialib_entry_t
00537  *
00538  */
00539 GTree *
00540 xmms_playlist_current_pos (xmms_playlist_t *playlist, const gchar *plname,
00541                            xmms_error_t *err)
00542 {
00543     guint32 pos;
00544     xmmsv_coll_t *plcoll;
00545     GTree *dict;
00546 
00547     g_return_val_if_fail (playlist, 0);
00548 
00549     g_mutex_lock (playlist->mutex);
00550 
00551     plcoll = xmms_playlist_get_coll (playlist, plname, err);
00552     if (plcoll == NULL) {
00553         g_mutex_unlock (playlist->mutex);
00554         xmms_error_set (err, XMMS_ERROR_INVAL, "no such playlist");
00555         return 0;
00556     }
00557 
00558     pos = xmms_playlist_coll_get_currpos (plcoll);
00559     if (pos == -1) {
00560         xmms_error_set (err, XMMS_ERROR_GENERIC, "no current entry");
00561     }
00562 
00563     g_mutex_unlock (playlist->mutex);
00564 
00565     dict = xmms_playlist_current_pos_msg_new (playlist, pos, plname);
00566 
00567     return dict;
00568 }
00569 
00570 /**
00571  * Retrieve a copy of the name of the currently active playlist.
00572  *
00573  */
00574 static gchar *
00575 xmms_playlist_current_active (xmms_playlist_t *playlist, xmms_error_t *err)
00576 {
00577     gchar *name = NULL;
00578     xmmsv_coll_t *active_coll;
00579 
00580     g_return_val_if_fail (playlist, 0);
00581 
00582     g_mutex_lock (playlist->mutex);
00583 
00584     active_coll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
00585     if (active_coll != NULL) {
00586         const gchar *alias;
00587 
00588         alias = xmms_collection_find_alias (playlist->colldag,
00589                                             XMMS_COLLECTION_NSID_PLAYLISTS,
00590                                             active_coll, XMMS_ACTIVE_PLAYLIST);
00591         if (alias == NULL) {
00592             xmms_error_set (err, XMMS_ERROR_GENERIC, "active playlist not referenced!");
00593         } else {
00594             name = g_strdup (alias);
00595         }
00596     } else {
00597         xmms_error_set (err, XMMS_ERROR_GENERIC, "no active playlist");
00598     }
00599 
00600     g_mutex_unlock (playlist->mutex);
00601 
00602     return name;
00603 }
00604 
00605 
00606 static void
00607 xmms_playlist_load (xmms_playlist_t *playlist, const gchar *name, xmms_error_t *err)
00608 {
00609     xmmsv_coll_t *plcoll, *active_coll;
00610 
00611     if (strcmp (name, XMMS_ACTIVE_PLAYLIST) == 0) {
00612         xmms_error_set (err, XMMS_ERROR_INVAL, "invalid playlist to load");
00613         return;
00614     }
00615 
00616     active_coll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
00617     if (active_coll == NULL) {
00618         xmms_error_set (err, XMMS_ERROR_GENERIC, "no active playlist");
00619         return;
00620     }
00621 
00622     plcoll = xmms_playlist_get_coll (playlist, name, err);
00623     if (plcoll == NULL) {
00624         xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist");
00625         return;
00626     }
00627 
00628     if (active_coll == plcoll) {
00629         XMMS_DBG ("Not loading %s playlist, already active!", name);
00630         return;
00631     }
00632 
00633     XMMS_DBG ("Loading new playlist! %s", name);
00634     xmms_collection_update_pointer (playlist->colldag, XMMS_ACTIVE_PLAYLIST,
00635                                     XMMS_COLLECTION_NSID_PLAYLISTS, plcoll);
00636 
00637     xmms_object_emit_f (XMMS_OBJECT (playlist),
00638                         XMMS_IPC_SIGNAL_PLAYLIST_LOADED,
00639                         XMMSV_TYPE_STRING,
00640                         name);
00641 }
00642 
00643 static inline void
00644 swap_entries (xmmsv_coll_t *coll, gint i, gint j)
00645 {
00646     guint32 tmp, tmp2;
00647 
00648     xmmsv_coll_idlist_get_index (coll, i, &tmp);
00649     xmmsv_coll_idlist_get_index (coll, j, &tmp2);
00650 
00651     xmmsv_coll_idlist_set_index (coll, i, tmp2);
00652     xmmsv_coll_idlist_set_index (coll, j, tmp);
00653 }
00654 
00655 
00656 /**
00657  * Shuffle the playlist.
00658  *
00659  */
00660 static void
00661 xmms_playlist_shuffle (xmms_playlist_t *playlist, const gchar *plname,
00662                        xmms_error_t *err)
00663 {
00664     guint j,i;
00665     gint len, currpos;
00666     xmmsv_coll_t *plcoll;
00667 
00668     g_return_if_fail (playlist);
00669 
00670     g_mutex_lock (playlist->mutex);
00671 
00672     plcoll = xmms_playlist_get_coll (playlist, plname, err);
00673     if (plcoll == NULL) {
00674         xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist");
00675         g_mutex_unlock (playlist->mutex);
00676         return;
00677     }
00678 
00679     currpos = xmms_playlist_coll_get_currpos (plcoll);
00680     len = xmms_playlist_coll_get_size (plcoll);
00681     if (len > 1) {
00682         /* put current at top and exclude from shuffling */
00683         if (currpos != -1) {
00684             swap_entries (plcoll, 0, currpos);
00685             currpos = 0;
00686             xmms_collection_set_int_attr (plcoll, "position", currpos);
00687         }
00688 
00689         /* knuth <3 */
00690         for (i = currpos + 1; i < len; i++) {
00691             j = g_random_int_range (i, len);
00692 
00693             if (i != j) {
00694                 swap_entries (plcoll, i, j);
00695             }
00696         }
00697 
00698     }
00699 
00700     XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SHUFFLE, 0, plname);
00701     XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
00702 
00703     g_mutex_unlock (playlist->mutex);
00704 }
00705 
00706 static gboolean
00707 xmms_playlist_remove_unlocked (xmms_playlist_t *playlist, const gchar *plname,
00708                                xmmsv_coll_t *plcoll, guint pos, xmms_error_t *err)
00709 {
00710     gint currpos;
00711     GTree *dict;
00712 
00713     g_return_val_if_fail (playlist, FALSE);
00714 
00715     currpos = xmms_playlist_coll_get_currpos (plcoll);
00716 
00717     if (!xmmsv_coll_idlist_remove (plcoll, pos)) {
00718         if (err) xmms_error_set (err, XMMS_ERROR_NOENT, "Entry was not in list!");
00719         return FALSE;
00720     }
00721 
00722     dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_REMOVE, 0, plname);
00723     g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
00724     xmms_playlist_changed_msg_send (playlist, dict);
00725 
00726     /* decrease currentpos if removed entry was before or if it's
00727      * the current entry, but only if currentpos is a valid entry.
00728      */
00729     if (currpos != -1 && pos <= currpos) {
00730         currpos--;
00731         xmms_collection_set_int_attr (plcoll, "position", currpos);
00732         XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
00733     }
00734 
00735     return TRUE;
00736 }
00737 
00738 typedef struct {
00739     xmms_playlist_t *pls;
00740     xmms_medialib_entry_t entry;
00741 } playlist_remove_info_t;
00742 
00743 static void
00744 remove_from_playlist (gpointer key, gpointer value, gpointer udata)
00745 {
00746     playlist_remove_info_t *rminfo = (playlist_remove_info_t *) udata;
00747     guint32 i, val;
00748     gint size;
00749     xmmsv_coll_t *plcoll = (xmmsv_coll_t *) value;
00750 
00751     size = xmms_playlist_coll_get_size (plcoll);
00752     for (i = 0; i < size; i++) {
00753         if (xmmsv_coll_idlist_get_index (plcoll, i, &val) && val == rminfo->entry) {
00754             XMMS_DBG ("removing entry on pos %d in %s", i, (gchar *)key);
00755             xmms_playlist_remove_unlocked (rminfo->pls, (gchar *)key, plcoll, i, NULL);
00756             i--; /* reset it */
00757         }
00758     }
00759 }
00760 
00761 
00762 
00763 /**
00764  * Remove all additions of entry in the playlist
00765  *
00766  * @param playlist the playlist to remove entries from
00767  * @param entry the playlist entry to remove
00768  *
00769  * @sa xmms_playlist_remove
00770  */
00771 gboolean
00772 xmms_playlist_remove_by_entry (xmms_playlist_t *playlist,
00773                                xmms_medialib_entry_t entry)
00774 {
00775     playlist_remove_info_t rminfo;
00776     g_return_val_if_fail (playlist, FALSE);
00777 
00778     g_mutex_lock (playlist->mutex);
00779 
00780     rminfo.pls = playlist;
00781     rminfo.entry = entry;
00782 
00783     xmms_collection_foreach_in_namespace (playlist->colldag,
00784                                           XMMS_COLLECTION_NSID_PLAYLISTS,
00785                                           remove_from_playlist, &rminfo);
00786 
00787     g_mutex_unlock (playlist->mutex);
00788 
00789     return TRUE;
00790 }
00791 
00792 /**
00793  * Remove an entry from playlist.
00794  *
00795  */
00796 gboolean
00797 xmms_playlist_remove (xmms_playlist_t *playlist, const gchar *plname,
00798                       guint pos, xmms_error_t *err)
00799 {
00800     gboolean ret = FALSE;
00801     xmmsv_coll_t *plcoll;
00802 
00803     g_return_val_if_fail (playlist, FALSE);
00804 
00805     g_mutex_lock (playlist->mutex);
00806     plcoll = xmms_playlist_get_coll (playlist, plname, err);
00807     if (plcoll != NULL) {
00808         ret = xmms_playlist_remove_unlocked (playlist, plname, plcoll, pos, err);
00809     }
00810     g_mutex_unlock (playlist->mutex);
00811     return ret;
00812 }
00813 
00814 
00815 /**
00816  * Move an entry in playlist
00817  *
00818  */
00819 static gboolean
00820 xmms_playlist_move (xmms_playlist_t *playlist, const gchar *plname, guint pos,
00821                     guint newpos, xmms_error_t *err)
00822 {
00823     GTree *dict;
00824     guint32 id;
00825     gint currpos, size;
00826     gint64 ipos, inewpos;
00827     xmmsv_coll_t *plcoll;
00828 
00829     g_return_val_if_fail (playlist, FALSE);
00830 
00831     XMMS_DBG ("Moving %d, to %d", pos, newpos);
00832 
00833     g_mutex_lock (playlist->mutex);
00834 
00835     plcoll = xmms_playlist_get_coll (playlist, plname, err);
00836     if (plcoll == NULL) {
00837         /* FIXME: happens ? */
00838         g_mutex_unlock (playlist->mutex);
00839         return FALSE;
00840     }
00841 
00842     currpos = xmms_playlist_coll_get_currpos (plcoll);
00843     size = xmms_playlist_coll_get_size (plcoll);
00844 
00845     if (size == 0 || newpos > (size - 1)) {
00846         xmms_error_set (err, XMMS_ERROR_NOENT,
00847                         "Cannot move entry outside playlist");
00848         g_mutex_unlock (playlist->mutex);
00849         return FALSE;
00850     }
00851 
00852     if (!xmmsv_coll_idlist_move (plcoll, pos, newpos)) {
00853         xmms_error_set (err, XMMS_ERROR_NOENT, "Entry was not in list!");
00854         g_mutex_unlock (playlist->mutex);
00855         return FALSE;
00856     }
00857 
00858     /* Update the current position pointer */
00859     ipos = pos;
00860     inewpos = newpos;
00861     if (inewpos <= currpos && ipos > currpos)
00862         currpos++;
00863     else if (inewpos >= currpos && ipos < currpos)
00864         currpos--;
00865     else if (ipos == currpos)
00866         currpos = inewpos;
00867 
00868     xmms_collection_set_int_attr (plcoll, "position", currpos);
00869 
00870     xmmsv_coll_idlist_get_index (plcoll, newpos, &id);
00871 
00872     dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_MOVE, id, plname);
00873     g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
00874     g_tree_insert (dict, (gpointer) "newposition", xmmsv_new_int (newpos));
00875     xmms_playlist_changed_msg_send (playlist, dict);
00876 
00877     XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
00878 
00879     g_mutex_unlock (playlist->mutex);
00880 
00881     return TRUE;
00882 
00883 }
00884 
00885 /**
00886  * Insert an entry into the playlist at given position.
00887  * Creates a #xmms_medialib_entry for you and insert it
00888  * in the list.
00889  *
00890  * @param playlist the playlist to add it URL to.
00891  * @param pos the position where the entry is inserted.
00892  * @param url the URL to add.
00893  * @param err an #xmms_error_t that should be defined upon error.
00894  * @return TRUE on success and FALSE otherwise.
00895  *
00896  */
00897 static gboolean
00898 xmms_playlist_insert_url (xmms_playlist_t *playlist, const gchar *plname,
00899                           guint32 pos, const gchar *url, xmms_error_t *err)
00900 {
00901     xmms_medialib_entry_t entry = 0;
00902     xmms_medialib_session_t *session = xmms_medialib_begin_write ();
00903 
00904     entry = xmms_medialib_entry_new_encoded (session, url, err);
00905     xmms_medialib_end (session);
00906 
00907     if (!entry) {
00908         return FALSE;
00909     }
00910 
00911     return xmms_playlist_insert_id (playlist, plname, pos, entry, err);
00912 }
00913 
00914 /**
00915   * Convenient function for inserting a directory at a given position
00916   * in the playlist, It will dive down the URL you feed it and
00917   * recursivly insert all files.
00918   *
00919   * @param playlist the playlist to add it URL to.
00920   * @param plname the name of the playlist to modify.
00921   * @param pos a position in the playlist.
00922   * @param nurl the URL of an directory you want to add
00923   * @param err an #xmms_error_t that should be defined upon error.
00924   */
00925 static void
00926 xmms_playlist_rinsert (xmms_playlist_t *playlist, const gchar *plname, guint32 pos,
00927                        const gchar *path, xmms_error_t *err)
00928 {
00929     /* we actually just call the medialib function, but keep
00930      * the ipc method here for not confusing users / developers
00931      */
00932     xmms_medialib_insert_recursive (playlist->medialib, plname, pos, path, err);
00933 }
00934 
00935 /**
00936  * Insert an xmms_medialib_entry to the playlist at given position.
00937  *
00938  * @param playlist the playlist to add the entry to.
00939  * @param pos the position where the entry is inserted.
00940  * @param file the #xmms_medialib_entry to add.
00941  * @param error Upon error this will be set.
00942  * @returns TRUE on success and FALSE otherwise.
00943  */
00944 static gboolean
00945 xmms_playlist_insert_id (xmms_playlist_t *playlist, const gchar *plname,
00946                          guint32 pos, xmms_medialib_entry_t file,
00947                          xmms_error_t *err)
00948 {
00949     if (!xmms_medialib_check_id (file)) {
00950         xmms_error_set (err, XMMS_ERROR_NOENT,
00951                         "That is not a valid medialib id!");
00952         return FALSE;
00953     }
00954 
00955     xmms_playlist_insert_entry (playlist, plname, pos, file, err);
00956 
00957     return TRUE;
00958 }
00959 
00960 static gboolean
00961 xmms_playlist_insert_collection (xmms_playlist_t *playlist, const gchar *plname,
00962                                  guint32 pos, xmmsv_coll_t *coll,
00963                                  xmmsv_t *order, xmms_error_t *err)
00964 {
00965     GList *res;
00966 
00967     res = xmms_collection_query_ids (playlist->colldag, coll, 0, 0, order, err);
00968 
00969     while (res) {
00970         xmmsv_t *val = (xmmsv_t*) res->data;
00971         gint id;
00972         xmmsv_get_int (val, &id);
00973         xmms_playlist_insert_id (playlist, plname, pos, id, err);
00974         xmmsv_unref (val);
00975 
00976         res = g_list_delete_link (res, res);
00977         pos++;
00978     }
00979 
00980     /* FIXME: detect errors? */
00981     return TRUE;
00982 }
00983 
00984 /**
00985  * Insert an entry at a given position in the playlist without
00986  * validating it.
00987  *
00988  * @internal
00989  */
00990 void
00991 xmms_playlist_insert_entry (xmms_playlist_t *playlist, const gchar *plname,
00992                             guint32 pos, xmms_medialib_entry_t file,
00993                             xmms_error_t *err)
00994 {
00995     GTree *dict;
00996     gint currpos;
00997     gint len;
00998     xmmsv_coll_t *plcoll;
00999 
01000     g_mutex_lock (playlist->mutex);
01001 
01002     plcoll = xmms_playlist_get_coll (playlist, plname, err);
01003     if (plcoll == NULL) {
01004         /* FIXME: happens ? */
01005         g_mutex_unlock (playlist->mutex);
01006         return;
01007     }
01008 
01009     len = xmms_playlist_coll_get_size (plcoll);
01010     if (pos > len || pos < 0) {
01011         xmms_error_set (err, XMMS_ERROR_GENERIC,
01012                         "Could not insert entry outside of playlist!");
01013         g_mutex_unlock (playlist->mutex);
01014         return;
01015     }
01016     xmmsv_coll_idlist_insert (plcoll, pos, file);
01017 
01018     /** propagate the MID ! */
01019     dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_INSERT, file, plname);
01020     g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
01021     xmms_playlist_changed_msg_send (playlist, dict);
01022 
01023     /** update position once client is familiar with the new item. */
01024     currpos = xmms_playlist_coll_get_currpos (plcoll);
01025     if (pos <= currpos) {
01026         currpos++;
01027         xmms_collection_set_int_attr (plcoll, "position", currpos);
01028         XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
01029     }
01030 
01031     g_mutex_unlock (playlist->mutex);
01032 }
01033 
01034 /**
01035   * Convenient function for adding a URL to the playlist,
01036   * Creates a #xmms_medialib_entry_t for you and adds it
01037   * to the list.
01038   *
01039   * @param playlist the playlist to add it URL to.
01040   * @param plname the name of the playlist to modify.
01041   * @param nurl the URL to add
01042   * @param err an #xmms_error_t that should be defined upon error.
01043   * @return TRUE on success and FALSE otherwise.
01044   */
01045 gboolean
01046 xmms_playlist_add_url (xmms_playlist_t *playlist, const gchar *plname,
01047                        const gchar *nurl, xmms_error_t *err)
01048 {
01049     xmms_medialib_entry_t entry = 0;
01050     xmms_medialib_session_t *session = xmms_medialib_begin_write ();
01051 
01052     entry = xmms_medialib_entry_new_encoded (session, nurl, err);
01053     xmms_medialib_end (session);
01054 
01055     if (entry) {
01056         xmms_playlist_add_entry (playlist, plname, entry, err);
01057     }
01058 
01059 
01060     return !!entry;
01061 }
01062 
01063 /**
01064   * Convenient function for adding a directory to the playlist,
01065   * It will dive down the URL you feed it and recursivly add
01066   * all files there.
01067   *
01068   * @param playlist the playlist to add it URL to.
01069   * @param plname the name of the playlist to modify.
01070   * @param nurl the URL of an directory you want to add
01071   * @param err an #xmms_error_t that should be defined upon error.
01072   */
01073 static void
01074 xmms_playlist_radd (xmms_playlist_t *playlist, const gchar *plname,
01075                     const gchar *path, xmms_error_t *err)
01076 {
01077     /* we actually just call the medialib function, but keep
01078      * the ipc method here for not confusing users / developers
01079      */
01080     xmms_medialib_add_recursive (playlist->medialib, plname, path, err);
01081 }
01082 
01083 /** Adds a xmms_medialib_entry to the playlist.
01084  *
01085  *  This will append or prepend the entry according to
01086  *  the option.
01087  *  This function will wake xmms_playlist_wait.
01088  *
01089  * @param playlist the playlist to add the entry to.
01090  * @param plname the name of the playlist to modify.
01091  * @param file the #xmms_medialib_entry_t to add
01092  * @param err Upon error this will be set.
01093  * @returns TRUE on success
01094  */
01095 
01096 gboolean
01097 xmms_playlist_add_id (xmms_playlist_t *playlist, const gchar *plname,
01098                       xmms_medialib_entry_t file, xmms_error_t *err)
01099 {
01100     if (!xmms_medialib_check_id (file)) {
01101         xmms_error_set (err, XMMS_ERROR_NOENT,
01102                         "That is not a valid medialib id!");
01103         return FALSE;
01104     }
01105 
01106     xmms_playlist_add_entry (playlist, plname, file, err);
01107 
01108     return TRUE;
01109 }
01110 
01111 gboolean
01112 xmms_playlist_add_idlist (xmms_playlist_t *playlist, const gchar *plname,
01113                           xmmsv_coll_t *coll, xmms_error_t *err)
01114 {
01115     uint32_t *idlist;
01116 
01117     for (idlist = xmmsv_coll_get_idlist (coll); *idlist; idlist++) {
01118         if (!xmms_medialib_check_id (*idlist)) {
01119             xmms_error_set (err, XMMS_ERROR_NOENT,
01120                             "Idlist contains invalid medialib id!");
01121             return FALSE;
01122         }
01123     }
01124 
01125     for (idlist = xmmsv_coll_get_idlist (coll); *idlist; idlist++) {
01126         xmms_playlist_add_entry (playlist, plname, *idlist, err);
01127     }
01128 
01129     return TRUE;
01130 }
01131 
01132 gboolean
01133 xmms_playlist_add_collection (xmms_playlist_t *playlist, const gchar *plname,
01134                               xmmsv_coll_t *coll, xmmsv_t *order,
01135                               xmms_error_t *err)
01136 {
01137     GList *res;
01138 
01139     res = xmms_collection_query_ids (playlist->colldag, coll, 0, 0, order, err);
01140 
01141     while (res) {
01142         xmmsv_t *val = (xmmsv_t*) res->data;
01143         gint id;
01144         xmmsv_get_int (val, &id);
01145         xmms_playlist_add_entry (playlist, plname, id, err);
01146         xmmsv_unref (val);
01147 
01148         res = g_list_delete_link (res, res);
01149     }
01150 
01151     /* FIXME: detect errors? */
01152     return TRUE;
01153 }
01154 
01155 /**
01156  * Add an entry to the playlist without validating it.
01157  *
01158  * @internal
01159  */
01160 void
01161 xmms_playlist_add_entry (xmms_playlist_t *playlist, const gchar *plname,
01162                          xmms_medialib_entry_t file, xmms_error_t *err)
01163 {
01164     xmmsv_coll_t *plcoll;
01165 
01166     g_mutex_lock (playlist->mutex);
01167 
01168     plcoll = xmms_playlist_get_coll (playlist, plname, err);
01169     if (plcoll != NULL) {
01170         xmms_playlist_add_entry_unlocked (playlist, plname, plcoll, file, err);
01171     }
01172 
01173     g_mutex_unlock (playlist->mutex);
01174 
01175 }
01176 
01177 /**
01178  * Add an entry to the playlist without locking the mutex.
01179  */
01180 void
01181 xmms_playlist_add_entry_unlocked (xmms_playlist_t *playlist,
01182                                   const gchar *plname,
01183                                   xmmsv_coll_t *plcoll,
01184                                   xmms_medialib_entry_t file,
01185                                   xmms_error_t *err)
01186 {
01187     gint prev_size;
01188     GTree *dict;
01189 
01190     prev_size = xmms_playlist_coll_get_size (plcoll);
01191     xmmsv_coll_idlist_append (plcoll, file);
01192 
01193     /** propagate the MID ! */
01194     dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_ADD, file, plname);
01195     g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (prev_size));
01196     xmms_playlist_changed_msg_send (playlist, dict);
01197 }
01198 
01199 /** Clear the playlist */
01200 static void
01201 xmms_playlist_clear (xmms_playlist_t *playlist, const gchar *plname,
01202                      xmms_error_t *err)
01203 {
01204     xmmsv_coll_t *plcoll;
01205 
01206     g_return_if_fail (playlist);
01207 
01208     g_mutex_lock (playlist->mutex);
01209 
01210     plcoll = xmms_playlist_get_coll (playlist, plname, err);
01211     if (plcoll == NULL) {
01212         g_mutex_unlock (playlist->mutex);
01213         return;
01214     }
01215 
01216     xmmsv_coll_idlist_clear (plcoll);
01217     xmms_collection_set_int_attr (plcoll, "position", -1);
01218 
01219     XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_CLEAR, 0, plname);
01220     g_mutex_unlock (playlist->mutex);
01221 
01222 }
01223 
01224 
01225 /** Set the nextentry pointer in the playlist.
01226  *
01227  *  This will set the pointer for the next entry to be
01228  *  returned by xmms_playlist_advance. This function
01229  *  will also wake xmms_playlist_wait
01230  */
01231 
01232 static gint
01233 xmms_playlist_set_current_position_do (xmms_playlist_t *playlist, guint32 pos,
01234                                        xmms_error_t *err)
01235 {
01236     gint size;
01237     guint mid;
01238     guint *idlist;
01239     xmmsv_coll_t *plcoll;
01240     char *jumplist;
01241 
01242     g_return_val_if_fail (playlist, FALSE);
01243 
01244     plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
01245     if (plcoll == NULL) {
01246         return 0;
01247     }
01248 
01249     size = xmms_playlist_coll_get_size (plcoll);
01250 
01251     if (pos == size &&
01252         xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) {
01253 
01254         xmms_collection_set_int_attr (plcoll, "position", 0);
01255         XMMS_PLAYLIST_CURRPOS_MSG (0, XMMS_ACTIVE_PLAYLIST);
01256 
01257         xmms_playlist_load (playlist, jumplist, err);
01258         if (xmms_error_iserror (err)) {
01259             return 0;
01260         }
01261 
01262         plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
01263         if (plcoll == NULL) {
01264             return 0;
01265         }
01266     } else if (pos < size) {
01267         XMMS_DBG ("newpos! %d", pos);
01268         xmms_collection_set_int_attr (plcoll, "position", pos);
01269         XMMS_PLAYLIST_CURRPOS_MSG (pos, XMMS_ACTIVE_PLAYLIST);
01270     } else {
01271         xmms_error_set (err, XMMS_ERROR_INVAL,
01272                         "Can't set pos outside the current playlist!");
01273         return 0;
01274     }
01275 
01276     idlist = xmmsv_coll_get_idlist (plcoll);
01277     mid = idlist[pos];
01278 
01279     return mid;
01280 }
01281 
01282 gint
01283 xmms_playlist_set_current_position (xmms_playlist_t *playlist, guint32 pos,
01284                                     xmms_error_t *err)
01285 {
01286     guint mid;
01287     g_return_val_if_fail (playlist, FALSE);
01288 
01289     g_mutex_lock (playlist->mutex);
01290     mid = xmms_playlist_set_current_position_do (playlist, pos, err);
01291     g_mutex_unlock (playlist->mutex);
01292 
01293     return mid;
01294 }
01295 
01296 static gint
01297 xmms_playlist_set_current_position_rel (xmms_playlist_t *playlist, gint32 pos,
01298                                         xmms_error_t *err)
01299 {
01300     gint currpos, newpos;
01301     guint mid = 0;
01302     xmmsv_coll_t *plcoll;
01303 
01304     g_return_val_if_fail (playlist, FALSE);
01305 
01306     g_mutex_lock (playlist->mutex);
01307 
01308     plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
01309     if (plcoll != NULL) {
01310         currpos = xmms_playlist_coll_get_currpos (plcoll);
01311 
01312         if (playlist->repeat_all) {
01313             newpos = (pos+currpos) % (gint)xmmsv_coll_idlist_get_size (plcoll);
01314 
01315             if (newpos < 0) {
01316                 newpos += xmmsv_coll_idlist_get_size (plcoll);
01317             }
01318 
01319             mid = xmms_playlist_set_current_position_do (playlist, newpos, err);
01320         } else {
01321             if (currpos + pos >= 0) {
01322                 mid = xmms_playlist_set_current_position_do (playlist,
01323                                                              currpos + pos,
01324                                                              err);
01325             } else {
01326                 xmms_error_set (err, XMMS_ERROR_INVAL,
01327                                 "Can't set pos outside the current playlist!");
01328             }
01329         }
01330     }
01331 
01332     g_mutex_unlock (playlist->mutex);
01333 
01334     return mid;
01335 }
01336 
01337 typedef struct {
01338     guint id;
01339     guint position;
01340     GList *val;  /* List of (xmmsv_t *) prop values */
01341     gboolean current;
01342 } sortdata_t;
01343 
01344 
01345 /**
01346  * Sort helper function.
01347  * Performs a case insesitive comparation between two entries.
01348  * We compare each pair of values in the list of prop values.
01349  */
01350 static gint
01351 xmms_playlist_entry_compare (gconstpointer a, gconstpointer b, gpointer user_data)
01352 {
01353     GList *n1, *n2;
01354     xmmsv_t *val1, *val2, *properties, *propval;
01355     xmmsv_list_iter_t *propit;
01356     sortdata_t *data1 = (sortdata_t *) a;
01357     sortdata_t *data2 = (sortdata_t *) b;
01358     int s1, s2, res;
01359     const gchar *propstr, *str1, *str2;
01360 
01361     properties = (xmmsv_t *) user_data;
01362     for (n1 = data1->val, n2 = data2->val, xmmsv_get_list_iter (properties, &propit);
01363          n1 && n2 && xmmsv_list_iter_valid (propit);
01364          n1 = n1->next, n2 = n2->next, xmmsv_list_iter_next (propit)) {
01365 
01366         xmmsv_list_iter_entry (propit, &propval);
01367         xmmsv_get_string (propval, &propstr);
01368         if (propstr[0] == '-') {
01369             val2 = n1->data;
01370             val1 = n2->data;
01371         } else {
01372             val1 = n1->data;
01373             val2 = n2->data;
01374         }
01375 
01376         if (!val1) {
01377             if (!val2)
01378                 continue;
01379             else
01380                 return -1;
01381         }
01382 
01383         if (!val2) {
01384             return 1;
01385         }
01386 
01387         if (xmmsv_get_type (val1) == XMMSV_TYPE_STRING &&
01388             xmmsv_get_type (val2) == XMMSV_TYPE_STRING) {
01389             xmmsv_get_string (val1, &str1);
01390             xmmsv_get_string (val2, &str2);
01391             res = g_utf8_collate (str1, str2);
01392             /* keep comparing next pair if equal */
01393             if (res == 0)
01394                 continue;
01395             else
01396                 return res;
01397         }
01398 
01399         if (xmmsv_get_type (val1) == XMMSV_TYPE_INT32 &&
01400             xmmsv_get_type (val2) == XMMSV_TYPE_INT32)
01401         {
01402             xmmsv_get_int (val1, &s1);
01403             xmmsv_get_int (val2, &s2);
01404 
01405             if (s1 < s2)
01406                 return -1;
01407             else if (s1 > s2)
01408                 return 1;
01409             else
01410                 continue;  /* equal, compare next pair of properties */
01411         }
01412 
01413         XMMS_DBG ("Types in compare function differ to much");
01414 
01415         return 0;
01416     }
01417 
01418     /* all pairs matched, really equal! */
01419     return 0;
01420 }
01421 
01422 /**
01423  * Unwind helper function.
01424  * Frees the sortdata elements.
01425  */
01426 static void
01427 xmms_playlist_sorted_free (gpointer data, gpointer userdata)
01428 {
01429     GList *n;
01430     sortdata_t *sorted = (sortdata_t *) data;
01431 
01432     for (n = sorted->val; n; n = n->next) {
01433         if (n->data) {
01434             xmmsv_unref (n->data);
01435         }
01436     }
01437     g_list_free (sorted->val);
01438     g_free (sorted);
01439 }
01440 
01441 /**
01442  * Unwind helper function.
01443  * Fills the playlist with the new sorted data.
01444  */
01445 static void
01446 xmms_playlist_sorted_unwind (gpointer data, gpointer userdata)
01447 {
01448     gint size;
01449     sortdata_t *sorted = (sortdata_t *) data;
01450     xmmsv_coll_t *playlist = (xmmsv_coll_t *)userdata;
01451 
01452     xmmsv_coll_idlist_append (playlist, sorted->id);
01453 
01454     if (sorted->current) {
01455         size = xmmsv_coll_idlist_get_size (playlist);
01456         xmms_collection_set_int_attr (playlist, "position", size - 1);
01457     }
01458 
01459     xmms_playlist_sorted_free (sorted, NULL);
01460 }
01461 
01462 /** Sorts the playlist by properties.
01463  *
01464  *  This will sort the list.
01465  *  @param playlist The playlist to sort.
01466  *  @param properties Tells xmms_playlist_sort which properties it
01467  *  should use when sorting.
01468  *  @param err An #xmms_error_t - needed since xmms_playlist_sort is an ipc
01469  *  method handler.
01470  */
01471 
01472 static void
01473 xmms_playlist_sort (xmms_playlist_t *playlist, const gchar *plname,
01474                     xmmsv_t *properties, xmms_error_t *err)
01475 {
01476     guint32 i;
01477     GList *tmp = NULL, *n;
01478     sortdata_t *data;
01479     const gchar *str;
01480     xmmsv_t *val;
01481     xmms_medialib_session_t *session;
01482     gboolean list_changed = FALSE;
01483     xmmsv_coll_t *plcoll;
01484     gint currpos, size;
01485     xmmsv_t *valstr;
01486     xmmsv_list_iter_t *propit;
01487 
01488     g_return_if_fail (playlist);
01489     g_return_if_fail (properties);
01490 
01491     g_mutex_lock (playlist->mutex);
01492 
01493     plcoll = xmms_playlist_get_coll (playlist, plname, err);
01494     if (plcoll == NULL) {
01495         xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist!");
01496         g_mutex_unlock (playlist->mutex);
01497         return;
01498     }
01499 
01500     /* check for invalid property strings */
01501     if (!check_string_list (properties)) {
01502         xmms_error_set (err, XMMS_ERROR_NOENT,
01503                         "invalid list of properties to sort by!");
01504         g_mutex_unlock (playlist->mutex);
01505         return;
01506     }
01507 
01508     if (xmmsv_list_get_size (properties) < 1) {
01509         xmms_error_set (err, XMMS_ERROR_NOENT,
01510                         "empty list of properties to sort by!");
01511         g_mutex_unlock (playlist->mutex);
01512         return;
01513     }
01514 
01515     /* in debug, show the first ordering property */
01516     xmmsv_list_get (properties, 0, &valstr);
01517     xmmsv_get_string (valstr, &str);
01518     XMMS_DBG ("Sorting on %s (and maybe more)", str);
01519 
01520     currpos = xmms_playlist_coll_get_currpos (plcoll);
01521     size = xmms_playlist_coll_get_size (plcoll);
01522 
01523     /* check whether we need to do any sorting at all */
01524     if (size < 2) {
01525         g_mutex_unlock (playlist->mutex);
01526         return;
01527     }
01528 
01529     session = xmms_medialib_begin ();
01530 
01531     xmmsv_get_list_iter (properties, &propit);
01532     for (i = 0; i < size; i++) {
01533         data = g_new (sortdata_t, 1);
01534 
01535         xmmsv_coll_idlist_get_index (plcoll, i, &data->id);
01536         data->position = i;
01537 
01538         /* save the list of values corresponding to the list of sort props */
01539         data->val = NULL;
01540         for (xmmsv_list_iter_first (propit);
01541              xmmsv_list_iter_valid (propit);
01542              xmmsv_list_iter_next (propit)) {
01543 
01544             xmmsv_list_iter_entry (propit, &valstr);
01545             xmmsv_get_string (valstr, &str);
01546             if (str[0] == '-')
01547                 str++;
01548 
01549             val = xmms_medialib_entry_property_get_value (session,
01550                                                           data->id,
01551                                                           str);
01552 
01553             if (val && xmmsv_get_type (val) == XMMSV_TYPE_STRING) {
01554                 gchar *casefold;
01555                 /* replace val by casefolded-string (beware of new/free order)*/
01556                 xmmsv_get_string (val, &str);
01557                 casefold = g_utf8_casefold (str, strlen (str));
01558                 xmmsv_unref (val);
01559 
01560                 val = xmmsv_new_string (casefold);
01561                 g_free (casefold);
01562             }
01563 
01564             data->val = g_list_prepend (data->val, val);
01565         }
01566         data->val = g_list_reverse (data->val);
01567 
01568         data->current = (currpos == i);
01569 
01570         tmp = g_list_prepend (tmp, data);
01571     }
01572 
01573     xmms_medialib_end (session);
01574 
01575     tmp = g_list_reverse (tmp);
01576     tmp = g_list_sort_with_data (tmp, xmms_playlist_entry_compare, properties);
01577 
01578     /* check whether there was any change */
01579     for (i = 0, n = tmp; n; i++, n = g_list_next (n)) {
01580         if (((sortdata_t*)n->data)->position != i) {
01581             list_changed = TRUE;
01582             break;
01583         }
01584     }
01585 
01586     if (!list_changed) {
01587         g_list_foreach (tmp, xmms_playlist_sorted_free, NULL);
01588         g_list_free (tmp);
01589         g_mutex_unlock (playlist->mutex);
01590         return;
01591     }
01592 
01593     xmmsv_coll_idlist_clear (plcoll);
01594     g_list_foreach (tmp, xmms_playlist_sorted_unwind, plcoll);
01595 
01596     g_list_free (tmp);
01597 
01598     XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SORT, 0, plname);
01599     XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
01600 
01601     g_mutex_unlock (playlist->mutex);
01602 }
01603 
01604 
01605 /** List a playlist */
01606 static GList *
01607 xmms_playlist_list_entries (xmms_playlist_t *playlist, const gchar *plname,
01608                             xmms_error_t *err)
01609 {
01610     GList *entries = NULL;
01611     xmmsv_coll_t *plcoll;
01612     guint *idlist;
01613     gint i;
01614 
01615     g_return_val_if_fail (playlist, NULL);
01616 
01617     g_mutex_lock (playlist->mutex);
01618 
01619     plcoll = xmms_playlist_get_coll (playlist, plname, err);
01620     if (plcoll == NULL) {
01621         g_mutex_unlock (playlist->mutex);
01622         return NULL;
01623     }
01624 
01625     idlist = xmmsv_coll_get_idlist (plcoll);
01626 
01627     for (i = 0; idlist[i] != 0; i++) {
01628         entries = g_list_prepend (entries, xmmsv_new_int (idlist[i]));
01629     }
01630 
01631     g_mutex_unlock (playlist->mutex);
01632 
01633     entries = g_list_reverse (entries);
01634 
01635     return entries;
01636 }
01637 
01638 /** returns pointer to mediainfo reader. */
01639 xmms_mediainfo_reader_t *
01640 xmms_playlist_mediainfo_reader_get (xmms_playlist_t *playlist)
01641 {
01642     g_return_val_if_fail (playlist, NULL);
01643 
01644     return playlist->mediainfordr;
01645 }
01646 
01647 /** @} */
01648 
01649 /** Free the playlist and other memory in the xmms_playlist_t
01650  *
01651  *  This will free all entries in the list!
01652  */
01653 
01654 static void
01655 xmms_playlist_destroy (xmms_object_t *object)
01656 {
01657     xmms_config_property_t *val;
01658     xmms_playlist_t *playlist = (xmms_playlist_t *)object;
01659 
01660     g_return_if_fail (playlist);
01661 
01662     g_mutex_free (playlist->mutex);
01663 
01664     val = xmms_config_lookup ("playlist.repeat_one");
01665     xmms_config_property_callback_remove (val, on_playlist_r_one_changed, playlist);
01666     val = xmms_config_lookup ("playlist.repeat_all");
01667     xmms_config_property_callback_remove (val, on_playlist_r_all_changed, playlist);
01668 
01669     xmms_object_unref (playlist->colldag);
01670     xmms_object_unref (playlist->mediainfordr);
01671 
01672     xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_PLAYLIST_CHANGED);
01673     xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS);
01674     xmms_ipc_object_unregister (XMMS_IPC_OBJECT_PLAYLIST);
01675 }
01676 
01677 
01678 static xmmsv_coll_t *
01679 xmms_playlist_get_coll (xmms_playlist_t *playlist, const gchar *plname,
01680                         xmms_error_t *error)
01681 {
01682     xmmsv_coll_t *coll;
01683     coll = xmms_collection_get_pointer (playlist->colldag, plname,
01684                                         XMMS_COLLECTION_NSID_PLAYLISTS);
01685 
01686     if (coll == NULL && error != NULL) {
01687         xmms_error_set (error, XMMS_ERROR_INVAL, "invalid playlist name");
01688     }
01689 
01690     return coll;
01691 }
01692 
01693 /**
01694  *  Retrieve the canonical name of a playlist. Assumes the playlist
01695  * name is valid.
01696  */
01697 static const gchar *
01698 xmms_playlist_canonical_name (xmms_playlist_t *playlist, const gchar *plname)
01699 {
01700     const gchar *fullname;
01701 
01702     if (strcmp (plname, XMMS_ACTIVE_PLAYLIST) == 0) {
01703         xmmsv_coll_t *coll;
01704         coll = xmms_collection_get_pointer (playlist->colldag, plname,
01705                                             XMMS_COLLECTION_NSID_PLAYLISTS);
01706         fullname = xmms_collection_find_alias (playlist->colldag,
01707                                                XMMS_COLLECTION_NSID_PLAYLISTS,
01708                                                coll, plname);
01709     } else {
01710         fullname = plname;
01711     }
01712 
01713     return fullname;
01714 }
01715 
01716 /** Get the current position in the given playlist (set to -1 if absent). */
01717 static gint
01718 xmms_playlist_coll_get_currpos (xmmsv_coll_t *plcoll)
01719 {
01720     gint currpos;
01721 
01722     /* If absent, set to -1 and save it */
01723     if (!xmms_collection_get_int_attr (plcoll, "position", &currpos)) {
01724         currpos = -1;
01725         xmms_collection_set_int_attr (plcoll, "position", currpos);
01726     }
01727 
01728     return currpos;
01729 }
01730 
01731 /** Get the size of the given playlist (compute and update it if absent). */
01732 static gint
01733 xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll)
01734 {
01735     return xmmsv_coll_idlist_get_size (plcoll);
01736 }
01737 
01738 
01739 GTree *
01740 xmms_playlist_changed_msg_new (xmms_playlist_t *playlist,
01741                                xmms_playlist_changed_actions_t type,
01742                                guint32 id, const gchar *plname)
01743 {
01744     GTree *dict;
01745     const gchar *tmp;
01746 
01747     dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
01748                             NULL, (GDestroyNotify) xmmsv_unref);
01749 
01750     g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type));
01751 
01752     if (id) {
01753         g_tree_insert (dict, (gpointer) "id", xmmsv_new_int (id));
01754     }
01755 
01756     tmp = xmms_playlist_canonical_name (playlist, plname);
01757     g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (tmp));
01758 
01759     return dict;
01760 }
01761 
01762 GTree *
01763 xmms_playlist_current_pos_msg_new (xmms_playlist_t *playlist,
01764                                    guint32 pos, const gchar *plname)
01765 {
01766     GTree *dict;
01767     const gchar *tmp;
01768 
01769     dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
01770                             NULL, (GDestroyNotify) xmmsv_unref);
01771 
01772     g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
01773 
01774     tmp = xmms_playlist_canonical_name (playlist, plname);
01775     g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (tmp));
01776 
01777     return dict;
01778 }
01779 
01780 void
01781 xmms_playlist_changed_msg_send (xmms_playlist_t *playlist, GTree *dict)
01782 {
01783     xmmsv_t *type_val;
01784     xmmsv_t *pl_val;
01785     gint type;
01786     const gchar *plname;
01787 
01788     g_return_if_fail (playlist);
01789     g_return_if_fail (dict);
01790 
01791     /* If local playlist change, trigger a COLL_CHANGED signal */
01792     type_val = g_tree_lookup (dict, "type");
01793     pl_val = g_tree_lookup (dict, "name");
01794     if (type_val != NULL && xmmsv_get_int (type_val, &type) &&
01795         type != XMMS_PLAYLIST_CHANGED_UPDATE &&
01796         pl_val != NULL && xmmsv_get_string (pl_val, &plname)) {
01797         XMMS_COLLECTION_PLAYLIST_CHANGED_MSG (playlist->colldag, plname);
01798     }
01799 
01800     xmms_object_emit_f (XMMS_OBJECT (playlist),
01801                         XMMS_IPC_SIGNAL_PLAYLIST_CHANGED,
01802                         XMMSV_TYPE_DICT,
01803                         dict);
01804 
01805     g_tree_destroy (dict);
01806 }
01807 
01808 static void
01809 xmms_playlist_current_pos_msg_send (xmms_playlist_t *playlist,
01810                                    GTree *dict)
01811 {
01812     g_return_if_fail (playlist);
01813 
01814     g_return_if_fail (dict);
01815 
01816     xmms_object_emit_f (XMMS_OBJECT (playlist),
01817                         XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS,
01818                         XMMSV_TYPE_DICT,
01819                         dict);
01820 
01821     g_tree_destroy (dict);
01822 }

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1