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