XMMS2

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