XMMS2
playlist.c
Go to the documentation of this file.
1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2011 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 */
16
17
18/** @file
19 * Controls playlist
20 */
21
22#include <stdio.h>
23#include <unistd.h>
24#include <stdlib.h>
25#include <string.h>
26#include <glib.h>
27#include <math.h>
28#include <ctype.h>
29
31#include "xmms/xmms_ipc.h"
32#include "xmms/xmms_config.h"
35#include "xmms/xmms_log.h"
36/*
37#include "xmms/plsplugins.h"
38#include "xmms/util.h"
39#include "xmms/signal_xmms.h"
40#include "xmms/ipc.h"
41#include "xmms/mediainfo.h"
42#include "xmms/magic.h"
43*/
44static void xmms_playlist_destroy (xmms_object_t *object);
45static void xmms_playlist_client_shuffle (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
46static void xmms_playlist_client_clear (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
47static void xmms_playlist_client_sort (xmms_playlist_t *playlist, const gchar *plname, xmmsv_t *property, xmms_error_t *err);
48static GList * xmms_playlist_client_list_entries (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
49static gchar *xmms_playlist_client_current_active (xmms_playlist_t *playlist, xmms_error_t *err);
50static void xmms_playlist_destroy (xmms_object_t *object);
51
52static void xmms_playlist_client_add_id (xmms_playlist_t *playlist, const gchar *plname, xmms_medialib_entry_t file, xmms_error_t *error);
53static void xmms_playlist_client_add_url (xmms_playlist_t *playlist, const gchar *plname, const gchar *nurl, xmms_error_t *err);
54static void xmms_playlist_client_add_idlist (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll, xmms_error_t *err);
55static void xmms_playlist_client_add_collection (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll, xmmsv_t *order, xmms_error_t *err);
56static GTree * xmms_playlist_client_current_pos (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
57static gint xmms_playlist_client_set_next (xmms_playlist_t *playlist, gint32 pos, xmms_error_t *error);
58static void xmms_playlist_client_remove_entry (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, xmms_error_t *err);
59static gboolean xmms_playlist_remove_unlocked (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *plcoll, guint pos, xmms_error_t *err);
60static void xmms_playlist_client_move_entry (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, gint32 newpos, xmms_error_t *err);
61static gint xmms_playlist_client_set_next_rel (xmms_playlist_t *playlist, gint32 pos, xmms_error_t *error);
62static gint xmms_playlist_set_current_position_do (xmms_playlist_t *playlist, guint32 pos, xmms_error_t *err);
63
64static void xmms_playlist_client_insert_url (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, const gchar *url, xmms_error_t *error);
65static void xmms_playlist_client_insert_id (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, xmms_medialib_entry_t file, xmms_error_t *error);
66static 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);
67static void xmms_playlist_client_radd (xmms_playlist_t *playlist, const gchar *plname, const gchar *path, xmms_error_t *error);
68static void xmms_playlist_client_rinsert (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, const gchar *path, xmms_error_t *error);
69
70static void xmms_playlist_client_load (xmms_playlist_t *, const gchar *, xmms_error_t *);
71
72static xmmsv_coll_t *xmms_playlist_get_coll (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *error);
73static const gchar *xmms_playlist_canonical_name (xmms_playlist_t *playlist, const gchar *plname);
74static gint xmms_playlist_coll_get_currpos (xmmsv_coll_t *plcoll);
75static gint xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll);
76
77static void xmms_playlist_update_queue (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll);
78static void xmms_playlist_update_partyshuffle (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll);
79static void xmms_playlist_register_ipc_commands (xmms_object_t *playlist_object);
80
81static void xmms_playlist_current_pos_msg_send (xmms_playlist_t *playlist, GTree *dict);
82static GTree * xmms_playlist_current_pos_msg_new (xmms_playlist_t *playlist, guint32 pos, const gchar *plname);
83
84#define XMMS_PLAYLIST_CHANGED_MSG(type, id, name) xmms_playlist_changed_msg_send (playlist, xmms_playlist_changed_msg_new (playlist, type, id, name))
85#define XMMS_PLAYLIST_CURRPOS_MSG(pos, name) xmms_playlist_current_pos_msg_send (playlist, xmms_playlist_current_pos_msg_new (playlist, pos, name))
86
87
88/** @defgroup Playlist Playlist
89 * @ingroup XMMSServer
90 * @brief This is the playlist control.
91 *
92 * A playlist is a central thing in the XMMS server, it
93 * tells us what to do after we played the following entry
94 * @{
95 */
96
97/** Playlist structure */
98struct xmms_playlist_St {
99 xmms_object_t object;
100
101 /* playlists are in the collection DAG */
102 xmms_coll_dag_t *colldag;
103
104 gboolean repeat_one;
105 gboolean repeat_all;
106
107 GMutex *mutex;
108
109 xmms_mediainfo_reader_t *mediainfordr;
110
111 gboolean update_flag;
112 xmms_medialib_t *medialib;
113};
114
115#include "playlist_ipc.c"
116
117static void
118on_playlist_r_all_changed (xmms_object_t *object, xmmsv_t *_data,
119 gpointer udata)
120{
121 xmms_playlist_t *playlist = udata;
122 gint value;
123
125
126 g_mutex_lock (playlist->mutex);
127 playlist->repeat_all = !!value;
128 g_mutex_unlock (playlist->mutex);
129}
130
131static void
132on_playlist_r_one_changed (xmms_object_t *object, xmmsv_t *_data,
133 gpointer udata)
134{
135 xmms_playlist_t *playlist = udata;
136 gint value;
137
139
140 g_mutex_lock (playlist->mutex);
141 playlist->repeat_one = !!value;
142 g_mutex_unlock (playlist->mutex);
143}
144
145
146static void
147on_playlist_updated (xmms_object_t *object, const gchar *plname)
148{
149 xmmsv_coll_t *plcoll;
150 xmms_playlist_t *playlist = (xmms_playlist_t*)object;
151
152 /* Already in an update process, quit */
153 if (playlist->update_flag) {
154 return;
155 }
156
157 plcoll = xmms_playlist_get_coll (playlist, plname, NULL);
158 if (plcoll == NULL) {
159 return;
160 } else {
161 /* Run the update function if appropriate */
162 switch (xmmsv_coll_get_type (plcoll)) {
164 xmms_playlist_update_queue (playlist, plname, plcoll);
165 break;
166
168 xmms_playlist_update_partyshuffle (playlist, plname, plcoll);
169 break;
170
171 default:
172 break;
173 }
174 }
175}
176
177static void
178on_playlist_updated_pos (xmms_object_t *object, xmmsv_t *val, gpointer udata)
179{
180 XMMS_DBG ("PLAYLIST: updated pos!");
181 on_playlist_updated (object, XMMS_ACTIVE_PLAYLIST);
182}
183
184static void
185on_playlist_updated_chg (xmms_object_t *object, xmmsv_t *val, gpointer udata)
186{
187 const gchar *plname = NULL;
188 xmmsv_t *pl_val;
189
190 XMMS_DBG ("PLAYLIST: updated chg!");
191
192 xmmsv_dict_get (val, "name", &pl_val);
193 if (pl_val != NULL) {
194 xmmsv_get_string (pl_val, &plname);
195 } else {
196 /* FIXME: occurs? */
197 XMMS_DBG ("PLAYLIST: updated_chg, NULL playlist!");
198 g_assert_not_reached ();
199 }
200
201 on_playlist_updated (object, plname);
202}
203
204static void
205xmms_playlist_update_queue (xmms_playlist_t *playlist, const gchar *plname,
206 xmmsv_coll_t *coll)
207{
208 gint history, currpos;
209
210 XMMS_DBG ("PLAYLIST: update-queue!");
211
212 if (!xmms_collection_get_int_attr (coll, "history", &history)) {
213 history = 0;
214 }
215
216 playlist->update_flag = TRUE;
217 currpos = xmms_playlist_coll_get_currpos (coll);
218 while (currpos > history) {
219 xmms_playlist_remove_unlocked (playlist, plname, coll, 0, NULL);
220 currpos = xmms_playlist_coll_get_currpos (coll);
221 }
222 playlist->update_flag = FALSE;
223}
224
225static void
226xmms_playlist_update_partyshuffle (xmms_playlist_t *playlist,
227 const gchar *plname, xmmsv_coll_t *coll)
228{
229 gint history, upcoming, currpos, size;
230 xmmsv_coll_t *src;
231 xmmsv_t *tmp;
232
233 XMMS_DBG ("PLAYLIST: update-partyshuffle!");
234
235 if (!xmms_collection_get_int_attr (coll, "history", &history)) {
236 history = 0;
237 }
238
239 if (!xmms_collection_get_int_attr (coll, "upcoming", &upcoming)) {
241 }
242
243 playlist->update_flag = TRUE;
244 currpos = xmms_playlist_coll_get_currpos (coll);
245 while (currpos > history) {
246 xmms_playlist_remove_unlocked (playlist, plname, coll, 0, NULL);
247 currpos = xmms_playlist_coll_get_currpos (coll);
248 }
249
250 if (!xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
251 XMMS_DBG ("Cannot find party shuffle operand!");
252 return;
253 }
254 xmmsv_get_coll (tmp, &src);
255
256 currpos = xmms_playlist_coll_get_currpos (coll);
257 size = xmms_playlist_coll_get_size (coll);
258 while (size < currpos + 1 + upcoming) {
259 xmms_medialib_entry_t randentry;
260 randentry = xmms_collection_get_random_media (playlist->colldag, src);
261 if (randentry == 0) {
262 break; /* No media found in the collection, give up */
263 }
264 /* FIXME: add_collection might yield better perf here. */
265 xmms_playlist_add_entry_unlocked (playlist, plname, coll, randentry, NULL);
266
267 currpos = xmms_playlist_coll_get_currpos (coll);
268 size = xmms_playlist_coll_get_size (coll);
269 }
270 playlist->update_flag = FALSE;
271}
272
273/**
274 * Initializes a new xmms_playlist_t.
275 */
278{
279 xmms_playlist_t *ret;
281
282 ret = xmms_object_new (xmms_playlist_t, xmms_playlist_destroy);
283 ret->mutex = g_mutex_new ();
284
285 xmms_playlist_register_ipc_commands (XMMS_OBJECT (ret));
286
287 val = xmms_config_property_register ("playlist.repeat_one", "0",
288 on_playlist_r_one_changed, ret);
289 ret->repeat_one = xmms_config_property_get_int (val);
290
291 val = xmms_config_property_register ("playlist.repeat_all", "0",
292 on_playlist_r_all_changed, ret);
293 ret->repeat_all = xmms_config_property_get_int (val);
294
295 ret->update_flag = FALSE;
296
299 on_playlist_updated_chg, ret);
300
303 on_playlist_updated_pos, ret);
304
305
306 ret->medialib = xmms_medialib_init (ret);
307 ret->colldag = xmms_collection_init (ret);
308 ret->mediainfordr = xmms_mediainfo_reader_start ();
309
310 return ret;
311}
312
313static gboolean
314xmms_playlist_advance_do (xmms_playlist_t *playlist)
315{
316 gint size, currpos;
317 gboolean ret = TRUE;
318 xmmsv_coll_t *plcoll;
319 char *jumplist;
320 xmms_error_t err;
321 xmms_playlist_t *buffer = playlist;
322 guint newpos;
323
324 xmms_error_reset (&err);
325
326 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, NULL);
327 if (plcoll == NULL) {
328 ret = FALSE;
329 } else if ((size = xmms_playlist_coll_get_size (plcoll)) == 0) {
330 if (xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) {
331 xmms_playlist_client_load (buffer, jumplist, &err);
332 if (xmms_error_isok (&err)) {
333 ret = xmms_playlist_advance_do (playlist);
334 } else {
335 ret = FALSE;
336 }
337 } else {
338 ret = FALSE;
339 }
340 } else if (!playlist->repeat_one) {
341 currpos = xmms_playlist_coll_get_currpos (plcoll);
342 currpos++;
343
344 if (currpos == size && !playlist->repeat_all &&
345 xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) {
346
347 xmms_collection_set_int_attr (plcoll, "position", -1);
349
350 xmms_playlist_client_load (buffer, jumplist, &err);
351 if (xmms_error_isok (&err)) {
352 ret = xmms_playlist_advance_do (playlist);
353 } else {
354 ret = FALSE;
355 }
356 } else {
357 newpos = currpos%size;
358 xmms_collection_set_int_attr (plcoll, "position", newpos);
360 ret = (currpos != size) || playlist->repeat_all;
361 }
362 }
363
364 return ret;
365}
366
367/**
368 * Go to next song in playlist according to current playlist mode.
369 * xmms_playlist_current_entry is to be used to retrieve the entry.
370 *
371 * @sa xmms_playlist_current_entry
372 *
373 * @returns FALSE if end of playlist is reached, TRUE otherwise.
374 */
375gboolean
377{
378 gboolean ret;
379
380 g_return_val_if_fail (playlist, FALSE);
381
382 g_mutex_lock (playlist->mutex);
383 ret = xmms_playlist_advance_do (playlist);
384 g_mutex_unlock (playlist->mutex);
385
386 return ret;
387}
388
389/**
390 * Retrieve the currently active xmms_medialib_entry_t.
391 *
392 */
395{
396 gint size, currpos;
397 xmmsv_coll_t *plcoll;
398 xmms_medialib_entry_t ent = 0;
399
400 g_return_val_if_fail (playlist, 0);
401
402 g_mutex_lock (playlist->mutex);
403
404 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, NULL);
405 if (plcoll == NULL) {
406 /* FIXME: What happens? */
407 g_mutex_unlock (playlist->mutex);
408 return 0;
409 }
410
411 currpos = xmms_playlist_coll_get_currpos (plcoll);
412 size = xmms_playlist_coll_get_size (plcoll);
413
414 if (currpos == -1 && (size > 0)) {
415 currpos = 0;
416 xmms_collection_set_int_attr (plcoll, "position", currpos);
418 }
419
420 if (currpos < size) {
421 xmmsv_coll_idlist_get_index (plcoll, currpos, &ent);
422 } else {
423 ent = 0;
424 }
425
426 g_mutex_unlock (playlist->mutex);
427
428 return ent;
429}
430
431
432/**
433 * Retrieve the position of the currently active xmms_medialib_entry_t
434 *
435 */
436GTree *
437xmms_playlist_client_current_pos (xmms_playlist_t *playlist, const gchar *plname,
438 xmms_error_t *err)
439{
440 guint32 pos;
441 xmmsv_coll_t *plcoll;
442 GTree *dict;
443
444 g_return_val_if_fail (playlist, 0);
445
446 g_mutex_lock (playlist->mutex);
447
448 plcoll = xmms_playlist_get_coll (playlist, plname, err);
449 if (plcoll == NULL) {
450 g_mutex_unlock (playlist->mutex);
451 xmms_error_set (err, XMMS_ERROR_INVAL, "no such playlist");
452 return 0;
453 }
454
455 pos = xmms_playlist_coll_get_currpos (plcoll);
456 if (pos == -1) {
457 xmms_error_set (err, XMMS_ERROR_GENERIC, "no current entry");
458 }
459
460 g_mutex_unlock (playlist->mutex);
461
462 dict = xmms_playlist_current_pos_msg_new (playlist, pos, plname);
463
464 return dict;
465}
466
467/**
468 * Retrieve a copy of the name of the currently active playlist.
469 *
470 */
471static gchar *
472xmms_playlist_client_current_active (xmms_playlist_t *playlist, xmms_error_t *err)
473{
474 gchar *name = NULL;
475 xmmsv_coll_t *active_coll;
476
477 g_return_val_if_fail (playlist, 0);
478
479 g_mutex_lock (playlist->mutex);
480
481 active_coll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
482 if (active_coll != NULL) {
483 const gchar *alias;
484
485 alias = xmms_collection_find_alias (playlist->colldag,
487 active_coll, XMMS_ACTIVE_PLAYLIST);
488 if (alias == NULL) {
489 xmms_error_set (err, XMMS_ERROR_GENERIC, "active playlist not referenced!");
490 } else {
491 name = g_strdup (alias);
492 }
493 } else {
494 xmms_error_set (err, XMMS_ERROR_GENERIC, "no active playlist");
495 }
496
497 g_mutex_unlock (playlist->mutex);
498
499 return name;
500}
501
502
503static void
504xmms_playlist_client_load (xmms_playlist_t *playlist, const gchar *name, xmms_error_t *err)
505{
506 xmmsv_coll_t *plcoll, *active_coll;
507
508 if (strcmp (name, XMMS_ACTIVE_PLAYLIST) == 0) {
509 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid playlist to load");
510 return;
511 }
512
513 active_coll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
514 if (active_coll == NULL) {
515 xmms_error_set (err, XMMS_ERROR_GENERIC, "no active playlist");
516 return;
517 }
518
519 plcoll = xmms_playlist_get_coll (playlist, name, err);
520 if (plcoll == NULL) {
521 xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist");
522 return;
523 }
524
525 if (active_coll == plcoll) {
526 XMMS_DBG ("Not loading %s playlist, already active!", name);
527 return;
528 }
529
530 XMMS_DBG ("Loading new playlist! %s", name);
533
537 name);
538}
539
540static inline void
541swap_entries (xmmsv_coll_t *coll, gint i, gint j)
542{
543 xmms_medialib_entry_t tmp, tmp2;
544
545 xmmsv_coll_idlist_get_index (coll, i, &tmp);
546 xmmsv_coll_idlist_get_index (coll, j, &tmp2);
547
548 xmmsv_coll_idlist_set_index (coll, i, tmp2);
549 xmmsv_coll_idlist_set_index (coll, j, tmp);
550}
551
552
553/**
554 * Shuffle the playlist.
555 *
556 */
557static void
558xmms_playlist_client_shuffle (xmms_playlist_t *playlist, const gchar *plname,
559 xmms_error_t *err)
560{
561 guint j,i;
562 gint len, currpos;
563 xmmsv_coll_t *plcoll;
564
565 g_return_if_fail (playlist);
566
567 g_mutex_lock (playlist->mutex);
568
569 plcoll = xmms_playlist_get_coll (playlist, plname, err);
570 if (plcoll == NULL) {
571 xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist");
572 g_mutex_unlock (playlist->mutex);
573 return;
574 }
575
576 currpos = xmms_playlist_coll_get_currpos (plcoll);
577 len = xmms_playlist_coll_get_size (plcoll);
578 if (len > 1) {
579 /* put current at top and exclude from shuffling */
580 if (currpos != -1) {
581 swap_entries (plcoll, 0, currpos);
582 currpos = 0;
583 xmms_collection_set_int_attr (plcoll, "position", currpos);
584 }
585
586 /* knuth <3 */
587 for (i = currpos + 1; i < len; i++) {
588 j = g_random_int_range (i, len);
589
590 if (i != j) {
591 swap_entries (plcoll, i, j);
592 }
593 }
594
595 }
596
598 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
599
600 g_mutex_unlock (playlist->mutex);
601}
602
603static gboolean
604xmms_playlist_remove_unlocked (xmms_playlist_t *playlist, const gchar *plname,
605 xmmsv_coll_t *plcoll, guint pos, xmms_error_t *err)
606{
607 gint currpos;
608 GTree *dict;
609
610 g_return_val_if_fail (playlist, FALSE);
611
612 currpos = xmms_playlist_coll_get_currpos (plcoll);
613
614 if (!xmmsv_coll_idlist_remove (plcoll, pos)) {
615 if (err) xmms_error_set (err, XMMS_ERROR_NOENT, "Entry was not in list!");
616 return FALSE;
617 }
618
620 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
621 xmms_playlist_changed_msg_send (playlist, dict);
622
623 /* decrease currentpos if removed entry was before or if it's
624 * the current entry, but only if currentpos is a valid entry.
625 */
626 if (currpos != -1 && pos <= currpos) {
627 currpos--;
628 xmms_collection_set_int_attr (plcoll, "position", currpos);
629 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
630 }
631
632 return TRUE;
633}
634
635typedef struct {
636 xmms_playlist_t *pls;
638} playlist_remove_info_t;
639
640static void
641remove_from_playlist (gpointer key, gpointer value, gpointer udata)
642{
643 playlist_remove_info_t *rminfo = (playlist_remove_info_t *) udata;
644 guint32 i;
646 gint size;
647 xmmsv_coll_t *plcoll = (xmmsv_coll_t *) value;
648
649 size = xmms_playlist_coll_get_size (plcoll);
650 for (i = 0; i < size; i++) {
651 if (xmmsv_coll_idlist_get_index (plcoll, i, &val) && val == rminfo->entry) {
652 XMMS_DBG ("removing entry on pos %d in %s", i, (gchar *)key);
653 xmms_playlist_remove_unlocked (rminfo->pls, (gchar *)key, plcoll, i, NULL);
654 i--; /* reset it */
655 }
656 }
657}
658
659
660
661/**
662 * Remove all additions of entry in the playlist
663 *
664 * @param playlist the playlist to remove entries from
665 * @param entry the playlist entry to remove
666 *
667 * @sa xmms_playlist_remove
668 */
669gboolean
672{
673 playlist_remove_info_t rminfo;
674 g_return_val_if_fail (playlist, FALSE);
675
676 g_mutex_lock (playlist->mutex);
677
678 rminfo.pls = playlist;
679 rminfo.entry = entry;
680
681 xmms_collection_foreach_in_namespace (playlist->colldag,
683 remove_from_playlist, &rminfo);
684
685 g_mutex_unlock (playlist->mutex);
686
687 return TRUE;
688}
689
690/**
691 * Remove an entry from playlist.
692 *
693 */
694void
695xmms_playlist_client_remove_entry (xmms_playlist_t *playlist,
696 const gchar *plname,
697 gint32 pos, xmms_error_t *err)
698{
699 gboolean ret = FALSE;
700 xmmsv_coll_t *plcoll;
701
702 g_return_if_fail (playlist);
703
704 g_mutex_lock (playlist->mutex);
705 plcoll = xmms_playlist_get_coll (playlist, plname, err);
706 if (plcoll != NULL) {
707 ret = xmms_playlist_remove_unlocked (playlist, plname, plcoll, pos, err);
708 }
709 g_mutex_unlock (playlist->mutex);
710}
711
712
713/**
714 * Move an entry in playlist
715 *
716 */
717static void
718xmms_playlist_client_move_entry (xmms_playlist_t *playlist,
719 const gchar *plname, gint32 pos,
720 gint32 newpos, xmms_error_t *err)
721{
722 GTree *dict;
724 gint currpos, size;
725 gint64 ipos, inewpos;
726 xmmsv_coll_t *plcoll;
727
728 g_return_if_fail (playlist);
729
730 XMMS_DBG ("Moving %d, to %d", pos, newpos);
731
732 g_mutex_lock (playlist->mutex);
733
734 plcoll = xmms_playlist_get_coll (playlist, plname, err);
735 if (plcoll == NULL) {
736 /* FIXME: happens ? */
737 g_mutex_unlock (playlist->mutex);
738 return;
739 }
740
741 currpos = xmms_playlist_coll_get_currpos (plcoll);
742 size = xmms_playlist_coll_get_size (plcoll);
743
744 if (size == 0 || newpos > (size - 1)) {
745 xmms_error_set (err, XMMS_ERROR_NOENT,
746 "Cannot move entry outside playlist");
747 g_mutex_unlock (playlist->mutex);
748 return;
749 }
750
751 if (!xmmsv_coll_idlist_move (plcoll, pos, newpos)) {
752 xmms_error_set (err, XMMS_ERROR_NOENT, "Entry was not in list!");
753 g_mutex_unlock (playlist->mutex);
754 return;
755 }
756
757 /* Update the current position pointer */
758 ipos = pos;
759 inewpos = newpos;
760 if (inewpos <= currpos && ipos > currpos)
761 currpos++;
762 else if (inewpos >= currpos && ipos < currpos)
763 currpos--;
764 else if (ipos == currpos)
765 currpos = inewpos;
766
767 xmms_collection_set_int_attr (plcoll, "position", currpos);
768
769 xmmsv_coll_idlist_get_index (plcoll, newpos, &id);
770
771 dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_MOVE, id, plname);
772 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
773 g_tree_insert (dict, (gpointer) "newposition", xmmsv_new_int (newpos));
774 xmms_playlist_changed_msg_send (playlist, dict);
775
776 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
777
778 g_mutex_unlock (playlist->mutex);
779
780 return;
781
782}
783
784/**
785 * Insert an entry into the playlist at given position.
786 * Creates a #xmms_medialib_entry for you and insert it
787 * in the list.
788 *
789 * @param playlist the playlist to add it URL to.
790 * @param pos the position where the entry is inserted.
791 * @param url the URL to add.
792 * @param err an #xmms_error_t that should be defined upon error.
793 * @return TRUE on success and FALSE otherwise.
794 *
795 */
796static void
797xmms_playlist_client_insert_url (xmms_playlist_t *playlist, const gchar *plname,
798 gint32 pos, const gchar *url, xmms_error_t *err)
799{
800 xmms_medialib_entry_t entry = 0;
802
803 entry = xmms_medialib_entry_new_encoded (session, url, err);
804 xmms_medialib_end (session);
805
806 if (!entry) {
807 return;
808 }
809
810 xmms_playlist_client_insert_id (playlist, plname, pos, entry, err);
811}
812
813/**
814 * Convenient function for inserting a directory at a given position
815 * in the playlist, It will dive down the URL you feed it and
816 * recursivly insert all files.
817 *
818 * @param playlist the playlist to add it URL to.
819 * @param plname the name of the playlist to modify.
820 * @param pos a position in the playlist.
821 * @param nurl the URL of an directory you want to add
822 * @param err an #xmms_error_t that should be defined upon error.
823 */
824static void
825xmms_playlist_client_rinsert (xmms_playlist_t *playlist, const gchar *plname, gint32 pos,
826 const gchar *path, xmms_error_t *err)
827{
828 /* we actually just call the medialib function, but keep
829 * the ipc method here for not confusing users / developers
830 */
831 xmms_medialib_insert_recursive (playlist->medialib, plname, pos, path, err);
832}
833
834/**
835 * Insert an xmms_medialib_entry to the playlist at given position.
836 *
837 * @param playlist the playlist to add the entry to.
838 * @param pos the position where the entry is inserted.
839 * @param file the #xmms_medialib_entry to add.
840 * @param error Upon error this will be set.
841 * @returns TRUE on success and FALSE otherwise.
842 */
843static void
844xmms_playlist_client_insert_id (xmms_playlist_t *playlist, const gchar *plname,
845 gint32 pos, xmms_medialib_entry_t file,
846 xmms_error_t *err)
847{
848 if (!xmms_medialib_check_id (file)) {
849 xmms_error_set (err, XMMS_ERROR_NOENT,
850 "That is not a valid medialib id!");
851 return;
852 }
853
854 xmms_playlist_insert_entry (playlist, plname, pos, file, err);
855}
856
857static void
858xmms_playlist_client_insert_collection (xmms_playlist_t *playlist, const gchar *plname,
859 gint32 pos, xmmsv_coll_t *coll,
860 xmmsv_t *order, xmms_error_t *err)
861{
862 GList *res;
863
864 res = xmms_collection_query_ids (playlist->colldag, coll, 0, 0, order, err);
865
866 while (res) {
867 xmmsv_t *val = (xmmsv_t*) res->data;
868 gint id;
869 xmmsv_get_int (val, &id);
870 xmms_playlist_client_insert_id (playlist, plname, pos, id, err);
871 xmmsv_unref (val);
872
873 res = g_list_delete_link (res, res);
874 pos++;
875 }
876
877}
878
879/**
880 * Insert an entry at a given position in the playlist without
881 * validating it.
882 *
883 * @internal
884 */
885void
886xmms_playlist_insert_entry (xmms_playlist_t *playlist, const gchar *plname,
887 guint32 pos, xmms_medialib_entry_t file,
888 xmms_error_t *err)
889{
890 GTree *dict;
891 gint currpos;
892 gint len;
893 xmmsv_coll_t *plcoll;
894
895 g_mutex_lock (playlist->mutex);
896
897 plcoll = xmms_playlist_get_coll (playlist, plname, err);
898 if (plcoll == NULL) {
899 /* FIXME: happens ? */
900 g_mutex_unlock (playlist->mutex);
901 return;
902 }
903
904 len = xmms_playlist_coll_get_size (plcoll);
905 if (pos > len) {
906 xmms_error_set (err, XMMS_ERROR_GENERIC,
907 "Could not insert entry outside of playlist!");
908 g_mutex_unlock (playlist->mutex);
909 return;
910 }
911 xmmsv_coll_idlist_insert (plcoll, pos, file);
912
913 /** propagate the MID ! */
914 dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_INSERT, file, plname);
915 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
916 xmms_playlist_changed_msg_send (playlist, dict);
917
918 /** update position once client is familiar with the new item. */
919 currpos = xmms_playlist_coll_get_currpos (plcoll);
920 if (pos <= currpos) {
921 currpos++;
922 xmms_collection_set_int_attr (plcoll, "position", currpos);
923 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
924 }
925
926 g_mutex_unlock (playlist->mutex);
927}
928
929/**
930 * Convenient function for adding a URL to the playlist,
931 * Creates a #xmms_medialib_entry_t for you and adds it
932 * to the list.
933 *
934 * @param playlist the playlist to add it URL to.
935 * @param plname the name of the playlist to modify.
936 * @param nurl the URL to add
937 * @param err an #xmms_error_t that should be defined upon error.
938 * @return TRUE on success and FALSE otherwise.
939 */
940void
941xmms_playlist_client_add_url (xmms_playlist_t *playlist, const gchar *plname,
942 const gchar *nurl, xmms_error_t *err)
943{
944 xmms_medialib_entry_t entry = 0;
946
947 entry = xmms_medialib_entry_new_encoded (session, nurl, err);
948 xmms_medialib_end (session);
949
950 if (entry) {
951 xmms_playlist_add_entry (playlist, plname, entry, err);
952 }
953
954}
955
956/**
957 * Convenient function for adding a directory to the playlist,
958 * It will dive down the URL you feed it and recursivly add
959 * all files there.
960 *
961 * @param playlist the playlist to add it URL to.
962 * @param plname the name of the playlist to modify.
963 * @param nurl the URL of an directory you want to add
964 * @param err an #xmms_error_t that should be defined upon error.
965 */
966static void
967xmms_playlist_client_radd (xmms_playlist_t *playlist, const gchar *plname,
968 const gchar *path, xmms_error_t *err)
969{
970 /* we actually just call the medialib function, but keep
971 * the ipc method here for not confusing users / developers
972 */
973 xmms_medialib_add_recursive (playlist->medialib, plname, path, err);
974}
975
976/** Adds a xmms_medialib_entry to the playlist.
977 *
978 * This will append or prepend the entry according to
979 * the option.
980 * This function will wake xmms_playlist_wait.
981 *
982 * @param playlist the playlist to add the entry to.
983 * @param plname the name of the playlist to modify.
984 * @param file the #xmms_medialib_entry_t to add
985 * @param err Upon error this will be set.
986 * @returns TRUE on success
987 */
988
989void
990xmms_playlist_client_add_id (xmms_playlist_t *playlist, const gchar *plname,
992{
993 if (!xmms_medialib_check_id (file)) {
994 xmms_error_set (err, XMMS_ERROR_NOENT,
995 "That is not a valid medialib id!");
996 return;
997 }
998
999 xmms_playlist_add_entry (playlist, plname, file, err);
1000}
1001
1002void
1003xmms_playlist_client_add_idlist (xmms_playlist_t *playlist,
1004 const gchar *plname,
1005 xmmsv_coll_t *coll, xmms_error_t *err)
1006{
1009
1011 for (xmmsv_list_iter_first (it);
1013 xmmsv_list_iter_next (it)) {
1014
1015 xmmsv_list_iter_entry_int (it, &entry);
1016 if (!xmms_medialib_check_id (entry)) {
1017 xmms_error_set (err, XMMS_ERROR_NOENT,
1018 "Idlist contains invalid medialib id!");
1020 return;
1021 }
1022 }
1023
1024 for (xmmsv_list_iter_first (it);
1026 xmmsv_list_iter_next (it)) {
1027
1028 xmmsv_list_iter_entry_int (it, &entry);
1029 xmms_playlist_add_entry (playlist, plname, entry, err);
1030 }
1032
1033}
1034
1035void
1036xmms_playlist_client_add_collection (xmms_playlist_t *playlist, const gchar *plname,
1037 xmmsv_coll_t *coll, xmmsv_t *order,
1038 xmms_error_t *err)
1039{
1040 GList *res;
1041
1042 res = xmms_collection_query_ids (playlist->colldag, coll, 0, 0, order, err);
1043
1044 while (res) {
1045 xmmsv_t *val = (xmmsv_t*) res->data;
1046 gint id;
1047 xmmsv_get_int (val, &id);
1048 xmms_playlist_add_entry (playlist, plname, id, err);
1049 xmmsv_unref (val);
1050
1051 res = g_list_delete_link (res, res);
1052 }
1053
1054}
1055
1056/**
1057 * Add an entry to the playlist without validating it.
1058 *
1059 * @internal
1060 */
1061void
1062xmms_playlist_add_entry (xmms_playlist_t *playlist, const gchar *plname,
1064{
1065 xmmsv_coll_t *plcoll;
1066
1067 g_mutex_lock (playlist->mutex);
1068
1069 plcoll = xmms_playlist_get_coll (playlist, plname, err);
1070 if (plcoll != NULL) {
1071 xmms_playlist_add_entry_unlocked (playlist, plname, plcoll, file, err);
1072 }
1073
1074 g_mutex_unlock (playlist->mutex);
1075
1076}
1077
1078/**
1079 * Add an entry to the playlist without locking the mutex.
1080 */
1081void
1083 const gchar *plname,
1084 xmmsv_coll_t *plcoll,
1086 xmms_error_t *err)
1087{
1088 gint prev_size;
1089 GTree *dict;
1090
1091 prev_size = xmms_playlist_coll_get_size (plcoll);
1092 xmmsv_coll_idlist_append (plcoll, file);
1093
1094 /** propagate the MID ! */
1095 dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_ADD, file, plname);
1096 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (prev_size));
1097 xmms_playlist_changed_msg_send (playlist, dict);
1098}
1099
1100/** Clear the playlist */
1101static void
1102xmms_playlist_client_clear (xmms_playlist_t *playlist, const gchar *plname,
1103 xmms_error_t *err)
1104{
1105 xmmsv_coll_t *plcoll;
1106
1107 g_return_if_fail (playlist);
1108
1109 g_mutex_lock (playlist->mutex);
1110
1111 plcoll = xmms_playlist_get_coll (playlist, plname, err);
1112 if (plcoll == NULL) {
1113 g_mutex_unlock (playlist->mutex);
1114 return;
1115 }
1116
1117 xmmsv_coll_idlist_clear (plcoll);
1118 xmms_collection_set_int_attr (plcoll, "position", -1);
1119
1121 g_mutex_unlock (playlist->mutex);
1122
1123}
1124
1125
1126/** Set the nextentry pointer in the playlist.
1127 *
1128 * This will set the pointer for the next entry to be
1129 * returned by xmms_playlist_advance. This function
1130 * will also wake xmms_playlist_wait
1131 */
1132
1133static gint
1134xmms_playlist_set_current_position_do (xmms_playlist_t *playlist, guint32 pos,
1135 xmms_error_t *err)
1136{
1137 gint size;
1139 xmmsv_coll_t *plcoll;
1140 char *jumplist;
1141
1142 g_return_val_if_fail (playlist, FALSE);
1143
1144 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
1145 if (plcoll == NULL) {
1146 return 0;
1147 }
1148
1149 size = xmms_playlist_coll_get_size (plcoll);
1150
1151 if (pos == size &&
1152 xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) {
1153
1154 xmms_collection_set_int_attr (plcoll, "position", 0);
1156
1157 xmms_playlist_client_load (playlist, jumplist, err);
1158 if (xmms_error_iserror (err)) {
1159 return 0;
1160 }
1161
1162 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
1163 if (plcoll == NULL) {
1164 return 0;
1165 }
1166 } else if (pos < size) {
1167 XMMS_DBG ("newpos! %d", pos);
1168 xmms_collection_set_int_attr (plcoll, "position", pos);
1170 } else {
1171 xmms_error_set (err, XMMS_ERROR_INVAL,
1172 "Can't set pos outside the current playlist!");
1173 return 0;
1174 }
1175
1176 xmmsv_coll_idlist_get_index (plcoll, pos, &mid);
1177
1178 return mid;
1179}
1180
1181gint
1182xmms_playlist_client_set_next (xmms_playlist_t *playlist, gint32 pos,
1183 xmms_error_t *err)
1184{
1186 g_return_val_if_fail (playlist, FALSE);
1187
1188 g_mutex_lock (playlist->mutex);
1189 mid = xmms_playlist_set_current_position_do (playlist, pos, err);
1190 g_mutex_unlock (playlist->mutex);
1191
1192 return mid;
1193}
1194
1195static gint
1196xmms_playlist_client_set_next_rel (xmms_playlist_t *playlist, gint32 pos,
1197 xmms_error_t *err)
1198{
1199 gint currpos, newpos, size;
1200 xmms_medialib_entry_t mid = 0;
1201 xmmsv_coll_t *plcoll;
1202
1203 g_return_val_if_fail (playlist, FALSE);
1204
1205 g_mutex_lock (playlist->mutex);
1206
1207 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err);
1208 if (plcoll != NULL) {
1209 currpos = xmms_playlist_coll_get_currpos (plcoll);
1210
1211 if (playlist->repeat_all) {
1212 newpos = pos + currpos;
1213 size = (gint) xmmsv_coll_idlist_get_size (plcoll);
1214
1215 if (size > 0) {
1216 newpos %= size;
1217 if (newpos < 0) {
1218 newpos += size;
1219 }
1220 }
1221
1222 mid = xmms_playlist_set_current_position_do (playlist, newpos, err);
1223 } else {
1224 if (currpos + pos >= 0) {
1225 mid = xmms_playlist_set_current_position_do (playlist,
1226 currpos + pos,
1227 err);
1228 } else {
1229 xmms_error_set (err, XMMS_ERROR_INVAL,
1230 "Can't set pos outside the current playlist!");
1231 }
1232 }
1233 }
1234
1235 g_mutex_unlock (playlist->mutex);
1236
1237 return mid;
1238}
1239
1240typedef struct {
1242 guint position;
1243 GList *val; /* List of (xmmsv_t *) prop values */
1244 gboolean current;
1245} sortdata_t;
1246
1247
1248/**
1249 * Sort helper function.
1250 * Performs a case insesitive comparation between two entries.
1251 * We compare each pair of values in the list of prop values.
1252 */
1253static gint
1254xmms_playlist_entry_compare (gconstpointer a, gconstpointer b, gpointer user_data)
1255{
1256 GList *n1, *n2;
1257 xmmsv_t *val1, *val2, *properties, *propval;
1258 xmmsv_list_iter_t *propit;
1259 sortdata_t *data1 = (sortdata_t *) a;
1260 sortdata_t *data2 = (sortdata_t *) b;
1261 int s1, s2, res;
1262 const gchar *propstr, *str1, *str2;
1263
1264 properties = (xmmsv_t *) user_data;
1265 for (n1 = data1->val, n2 = data2->val, xmmsv_get_list_iter (properties, &propit);
1266 n1 && n2 && xmmsv_list_iter_valid (propit);
1267 n1 = n1->next, n2 = n2->next, xmmsv_list_iter_next (propit)) {
1268
1269 xmmsv_list_iter_entry (propit, &propval);
1270 xmmsv_get_string (propval, &propstr);
1271 if (propstr[0] == '-') {
1272 val2 = n1->data;
1273 val1 = n2->data;
1274 } else {
1275 val1 = n1->data;
1276 val2 = n2->data;
1277 }
1278
1279 if (!val1) {
1280 if (!val2)
1281 continue;
1282 else
1283 return -1;
1284 }
1285
1286 if (!val2) {
1287 return 1;
1288 }
1289
1290 if (xmmsv_get_type (val1) == XMMSV_TYPE_STRING &&
1292 xmmsv_get_string (val1, &str1);
1293 xmmsv_get_string (val2, &str2);
1294 res = g_utf8_collate (str1, str2);
1295 /* keep comparing next pair if equal */
1296 if (res == 0)
1297 continue;
1298 else
1299 return res;
1300 }
1301
1302 if (xmmsv_get_type (val1) == XMMSV_TYPE_INT32 &&
1304 {
1305 xmmsv_get_int (val1, &s1);
1306 xmmsv_get_int (val2, &s2);
1307
1308 if (s1 < s2)
1309 return -1;
1310 else if (s1 > s2)
1311 return 1;
1312 else
1313 continue; /* equal, compare next pair of properties */
1314 }
1315
1316 XMMS_DBG ("Types in compare function differ to much");
1317
1318 return 0;
1319 }
1320
1321 /* all pairs matched, really equal! */
1322 return 0;
1323}
1324
1325/**
1326 * Unwind helper function.
1327 * Frees the sortdata elements.
1328 */
1329static void
1330xmms_playlist_sorted_free (gpointer data, gpointer userdata)
1331{
1332 GList *n;
1333 sortdata_t *sorted = (sortdata_t *) data;
1334
1335 for (n = sorted->val; n; n = n->next) {
1336 if (n->data) {
1337 xmmsv_unref (n->data);
1338 }
1339 }
1340 g_list_free (sorted->val);
1341 g_free (sorted);
1342}
1343
1344/**
1345 * Unwind helper function.
1346 * Fills the playlist with the new sorted data.
1347 */
1348static void
1349xmms_playlist_sorted_unwind (gpointer data, gpointer userdata)
1350{
1351 gint size;
1352 sortdata_t *sorted = (sortdata_t *) data;
1353 xmmsv_coll_t *playlist = (xmmsv_coll_t *)userdata;
1354
1355 xmmsv_coll_idlist_append (playlist, sorted->id);
1356
1357 if (sorted->current) {
1358 size = xmmsv_coll_idlist_get_size (playlist);
1359 xmms_collection_set_int_attr (playlist, "position", size - 1);
1360 }
1361
1362 xmms_playlist_sorted_free (sorted, NULL);
1363}
1364
1365/** Sorts the playlist by properties.
1366 *
1367 * This will sort the list.
1368 * @param playlist The playlist to sort.
1369 * @param properties Tells xmms_playlist_sort which properties it
1370 * should use when sorting.
1371 * @param err An #xmms_error_t - needed since xmms_playlist_sort is an ipc
1372 * method handler.
1373 */
1374
1375static void
1376xmms_playlist_client_sort (xmms_playlist_t *playlist, const gchar *plname,
1377 xmmsv_t *properties, xmms_error_t *err)
1378{
1379 guint32 i;
1380 GList *tmp = NULL, *n;
1381 sortdata_t *data;
1382 const gchar *str;
1383 xmmsv_t *val;
1384 xmms_medialib_session_t *session;
1385 gboolean list_changed = FALSE;
1386 xmmsv_coll_t *plcoll;
1387 gint currpos, size;
1388 xmmsv_t *valstr;
1389 xmmsv_list_iter_t *propit;
1390
1391 g_return_if_fail (playlist);
1392 g_return_if_fail (properties);
1393
1394 g_mutex_lock (playlist->mutex);
1395
1396 plcoll = xmms_playlist_get_coll (playlist, plname, err);
1397 if (plcoll == NULL) {
1398 xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist!");
1399 g_mutex_unlock (playlist->mutex);
1400 return;
1401 }
1402
1403 /* check for invalid property strings */
1404 if (!check_string_list (properties)) {
1405 xmms_error_set (err, XMMS_ERROR_NOENT,
1406 "invalid list of properties to sort by!");
1407 g_mutex_unlock (playlist->mutex);
1408 return;
1409 }
1410
1411 if (xmmsv_list_get_size (properties) < 1) {
1412 xmms_error_set (err, XMMS_ERROR_NOENT,
1413 "empty list of properties to sort by!");
1414 g_mutex_unlock (playlist->mutex);
1415 return;
1416 }
1417
1418 /* in debug, show the first ordering property */
1419 xmmsv_list_get (properties, 0, &valstr);
1420 xmmsv_get_string (valstr, &str);
1421 XMMS_DBG ("Sorting on %s (and maybe more)", str);
1422
1423 currpos = xmms_playlist_coll_get_currpos (plcoll);
1424 size = xmms_playlist_coll_get_size (plcoll);
1425
1426 /* check whether we need to do any sorting at all */
1427 if (size < 2) {
1428 g_mutex_unlock (playlist->mutex);
1429 return;
1430 }
1431
1432 session = xmms_medialib_begin ();
1433
1434 xmmsv_get_list_iter (properties, &propit);
1435 for (i = 0; i < size; i++) {
1436 data = g_new (sortdata_t, 1);
1437
1438 xmmsv_coll_idlist_get_index (plcoll, i, &data->id);
1439 data->position = i;
1440
1441 /* save the list of values corresponding to the list of sort props */
1442 data->val = NULL;
1443 for (xmmsv_list_iter_first (propit);
1444 xmmsv_list_iter_valid (propit);
1445 xmmsv_list_iter_next (propit)) {
1446
1447 xmmsv_list_iter_entry (propit, &valstr);
1448 xmmsv_get_string (valstr, &str);
1449 if (str[0] == '-')
1450 str++;
1451
1453 data->id,
1454 str);
1455
1456 if (val && xmmsv_get_type (val) == XMMSV_TYPE_STRING) {
1457 gchar *casefold;
1458 /* replace val by casefolded-string (beware of new/free order)*/
1459 xmmsv_get_string (val, &str);
1460 casefold = g_utf8_casefold (str, strlen (str));
1461 xmmsv_unref (val);
1462
1463 val = xmmsv_new_string (casefold);
1464 g_free (casefold);
1465 }
1466
1467 data->val = g_list_prepend (data->val, val);
1468 }
1469 data->val = g_list_reverse (data->val);
1470
1471 data->current = (currpos == i);
1472
1473 tmp = g_list_prepend (tmp, data);
1474 }
1475
1476 xmms_medialib_end (session);
1477
1478 tmp = g_list_reverse (tmp);
1479 tmp = g_list_sort_with_data (tmp, xmms_playlist_entry_compare, properties);
1480
1481 /* check whether there was any change */
1482 for (i = 0, n = tmp; n; i++, n = g_list_next (n)) {
1483 if (((sortdata_t*)n->data)->position != i) {
1484 list_changed = TRUE;
1485 break;
1486 }
1487 }
1488
1489 if (!list_changed) {
1490 g_list_foreach (tmp, xmms_playlist_sorted_free, NULL);
1491 g_list_free (tmp);
1492 g_mutex_unlock (playlist->mutex);
1493 return;
1494 }
1495
1496 xmmsv_coll_idlist_clear (plcoll);
1497 g_list_foreach (tmp, xmms_playlist_sorted_unwind, plcoll);
1498
1499 g_list_free (tmp);
1500
1502 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);
1503
1504 g_mutex_unlock (playlist->mutex);
1505}
1506
1507
1508/** List a playlist */
1509static GList *
1510xmms_playlist_client_list_entries (xmms_playlist_t *playlist, const gchar *plname,
1511 xmms_error_t *err)
1512{
1513 GList *entries = NULL;
1514 xmmsv_coll_t *plcoll;
1517
1518 g_return_val_if_fail (playlist, NULL);
1519
1520 g_mutex_lock (playlist->mutex);
1521
1522 plcoll = xmms_playlist_get_coll (playlist, plname, err);
1523 if (plcoll == NULL) {
1524 g_mutex_unlock (playlist->mutex);
1525 return NULL;
1526 }
1527
1529 for (xmmsv_list_iter_first (it);
1531 xmmsv_list_iter_next (it)) {
1532
1533 xmmsv_list_iter_entry_int (it, &entry);
1534 entries = g_list_prepend (entries, xmmsv_new_int (entry));
1535 }
1537
1538 g_mutex_unlock (playlist->mutex);
1539
1540 entries = g_list_reverse (entries);
1541
1542 return entries;
1543}
1544
1545/** returns pointer to mediainfo reader. */
1548{
1549 g_return_val_if_fail (playlist, NULL);
1550
1551 return playlist->mediainfordr;
1552}
1553
1554/** @} */
1555
1556/** Free the playlist and other memory in the xmms_playlist_t
1557 *
1558 * This will free all entries in the list!
1559 */
1560
1561static void
1562xmms_playlist_destroy (xmms_object_t *object)
1563{
1565 xmms_playlist_t *playlist = (xmms_playlist_t *)object;
1566
1567 g_return_if_fail (playlist);
1568
1569 g_mutex_free (playlist->mutex);
1570
1571 val = xmms_config_lookup ("playlist.repeat_one");
1572 xmms_config_property_callback_remove (val, on_playlist_r_one_changed, playlist);
1573 val = xmms_config_lookup ("playlist.repeat_all");
1574 xmms_config_property_callback_remove (val, on_playlist_r_all_changed, playlist);
1575
1576 xmms_object_unref (playlist->colldag);
1577 xmms_object_unref (playlist->mediainfordr);
1578
1579 xmms_playlist_unregister_ipc_commands ();
1580}
1581
1582
1583static xmmsv_coll_t *
1584xmms_playlist_get_coll (xmms_playlist_t *playlist, const gchar *plname,
1585 xmms_error_t *error)
1586{
1587 xmmsv_coll_t *coll;
1588 coll = xmms_collection_get_pointer (playlist->colldag, plname,
1590
1591 if (coll == NULL && error != NULL) {
1592 xmms_error_set (error, XMMS_ERROR_INVAL, "invalid playlist name");
1593 }
1594
1595 return coll;
1596}
1597
1598/**
1599 * Retrieve the canonical name of a playlist. Assumes the playlist
1600 * name is valid.
1601 */
1602static const gchar *
1603xmms_playlist_canonical_name (xmms_playlist_t *playlist, const gchar *plname)
1604{
1605 const gchar *fullname;
1606
1607 if (strcmp (plname, XMMS_ACTIVE_PLAYLIST) == 0) {
1608 xmmsv_coll_t *coll;
1609 coll = xmms_collection_get_pointer (playlist->colldag, plname,
1611 fullname = xmms_collection_find_alias (playlist->colldag,
1613 coll, plname);
1614 } else {
1615 fullname = plname;
1616 }
1617
1618 return fullname;
1619}
1620
1621/** Get the current position in the given playlist (set to -1 if absent). */
1622static gint
1623xmms_playlist_coll_get_currpos (xmmsv_coll_t *plcoll)
1624{
1625 gint currpos;
1626
1627 /* If absent, set to -1 and save it */
1628 if (!xmms_collection_get_int_attr (plcoll, "position", &currpos)) {
1629 currpos = -1;
1630 xmms_collection_set_int_attr (plcoll, "position", currpos);
1631 }
1632
1633 return currpos;
1634}
1635
1636/** Get the size of the given playlist (compute and update it if absent). */
1637static gint
1638xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll)
1639{
1640 return xmmsv_coll_idlist_get_size (plcoll);
1641}
1642
1643
1644GTree *
1647 xmms_medialib_entry_t id, const gchar *plname)
1648{
1649 GTree *dict;
1650 const gchar *tmp;
1651
1652 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
1653 NULL, (GDestroyNotify) xmmsv_unref);
1654
1655 g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type));
1656
1657 if (id) {
1658 g_tree_insert (dict, (gpointer) "id", xmmsv_new_int (id));
1659 }
1660
1661 tmp = xmms_playlist_canonical_name (playlist, plname);
1662 g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (tmp));
1663
1664 return dict;
1665}
1666
1667GTree *
1668xmms_playlist_current_pos_msg_new (xmms_playlist_t *playlist,
1669 guint32 pos, const gchar *plname)
1670{
1671 GTree *dict;
1672 const gchar *tmp;
1673
1674 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
1675 NULL, (GDestroyNotify) xmmsv_unref);
1676
1677 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos));
1678
1679 tmp = xmms_playlist_canonical_name (playlist, plname);
1680 g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (tmp));
1681
1682 return dict;
1683}
1684
1685void
1687{
1688 xmmsv_t *type_val;
1689 xmmsv_t *pl_val;
1690 gint type;
1691 const gchar *plname;
1692
1693 g_return_if_fail (playlist);
1694 g_return_if_fail (dict);
1695
1696 /* If local playlist change, trigger a COLL_CHANGED signal */
1697 type_val = g_tree_lookup (dict, "type");
1698 pl_val = g_tree_lookup (dict, "name");
1699 if (type_val != NULL && xmmsv_get_int (type_val, &type) &&
1701 pl_val != NULL && xmmsv_get_string (pl_val, &plname)) {
1702 XMMS_COLLECTION_PLAYLIST_CHANGED_MSG (playlist->colldag, plname);
1703 }
1704
1705 xmms_object_emit_f (XMMS_OBJECT (playlist),
1708 dict);
1709
1710 g_tree_destroy (dict);
1711}
1712
1713static void
1714xmms_playlist_current_pos_msg_send (xmms_playlist_t *playlist,
1715 GTree *dict)
1716{
1717 g_return_if_fail (playlist);
1718
1719 g_return_if_fail (dict);
1720
1721 xmms_object_emit_f (XMMS_OBJECT (playlist),
1724 dict);
1725
1726 g_tree_destroy (dict);
1727}
void xmms_collection_foreach_in_namespace(xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
Apply a function to all the collections in a given namespace.
struct xmmsv_St * xmmsv_coll_idlist_get(xmmsv_coll_t *coll)
Return the list of ids stored in the collection.
Definition coll.c:429
int xmmsv_coll_idlist_remove(xmmsv_coll_t *coll, int index)
Remove the value at a given index from the idlist.
Definition coll.c:296
xmmsv_coll_type_t xmmsv_coll_get_type(xmmsv_coll_t *coll)
Return the type of the collection.
Definition coll.c:367
struct xmmsv_St * xmmsv_coll_operands_get(xmmsv_coll_t *coll)
Definition coll.c:437
int xmmsv_coll_idlist_clear(xmmsv_coll_t *coll)
Empties the idlist.
Definition coll.c:309
size_t xmmsv_coll_idlist_get_size(xmmsv_coll_t *coll)
Get the size of the idlist.
Definition coll.c:352
int xmmsv_coll_idlist_insert(xmmsv_coll_t *coll, int index, int id)
Insert a value at a given position in the idlist.
Definition coll.c:267
int xmmsv_coll_idlist_get_index(xmmsv_coll_t *coll, int index, int32_t *val)
Retrieves the value at the given position in the idlist.
Definition coll.c:324
int xmmsv_coll_idlist_append(xmmsv_coll_t *coll, int id)
Append a value to the idlist.
Definition coll.c:252
int xmmsv_coll_idlist_set_index(xmmsv_coll_t *coll, int index, int32_t val)
Sets the value at the given position in the idlist.
Definition coll.c:339
int xmmsv_coll_attribute_get(xmmsv_coll_t *coll, const char *key, char **value)
Retrieve the value of the attribute of the given collection.
Definition coll.c:498
int xmmsv_coll_idlist_move(xmmsv_coll_t *coll, int index, int newindex)
Move a value of the idlist to a new position.
Definition coll.c:282
gboolean xmms_collection_set_int_attr(xmmsv_coll_t *coll, const gchar *attrname, gint newval)
Set the attribute of a collection as an integer.
Definition collection.c:926
gboolean xmms_collection_get_int_attr(xmmsv_coll_t *coll, const gchar *attrname, gint *val)
Extract an attribute from a collection as an integer.
Definition collection.c:898
void xmms_collection_update_pointer(xmms_coll_dag_t *dag, const gchar *name, guint nsid, xmmsv_coll_t *newtarget)
Update a reference to point to a new collection.
Definition collection.c:849
xmms_medialib_entry_t xmms_collection_get_random_media(xmms_coll_dag_t *dag, xmmsv_coll_t *source)
Get a random media entry from the given collection.
Definition collection.c:978
const gchar * xmms_collection_find_alias(xmms_coll_dag_t *dag, guint nsid, xmmsv_coll_t *value, const gchar *key)
Reverse-search the list of collections in the given namespace to find the first pair whose value matc...
Definition collection.c:955
xmmsv_coll_t * xmms_collection_get_pointer(xmms_coll_dag_t *dag, const gchar *collname, guint nsid)
Find the collection structure corresponding to the given name in the given namespace.
Definition collection.c:873
xmms_coll_dag_t * xmms_collection_init(xmms_playlist_t *playlist)
Initializes a new xmms_coll_dag_t.
Definition collection.c:214
GList * xmms_collection_query_ids(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmms_error_t *err)
Find the ids of the media matched by a collection.
Definition collection.c:731
void xmms_config_property_callback_remove(xmms_config_property_t *prop, xmms_object_handler_t cb, gpointer userdata)
Remove a callback from a config property.
Definition config.c:307
xmms_config_property_t * xmms_config_property_register(const gchar *path, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a new config property.
Definition config.c:334
gint xmms_config_property_get_int(const xmms_config_property_t *prop)
Return the value of a config property as an int.
Definition config.c:255
xmms_config_property_t * xmms_config_lookup(const gchar *path)
Look up a config key from the global config.
Definition config.c:171
int xmmsv_dict_get(xmmsv_t *dictv, const char *key, xmmsv_t **val)
Get the element corresponding to the given key in the dict xmmsv_t (if it exists).
Definition value.c:1717
int xmmsv_get_list_iter(const xmmsv_t *val, xmmsv_list_iter_t **it)
Retrieves a list iterator from a list xmmsv_t.
Definition value.c:926
void xmmsv_list_iter_next(xmmsv_list_iter_t *it)
Advance the iterator to the next element in the list.
Definition value.c:1553
struct xmmsv_list_iter_St xmmsv_list_iter_t
Definition xmmsv_list.h:69
int xmmsv_list_iter_entry(xmmsv_list_iter_t *it, xmmsv_t **val)
Get the element currently pointed at by the iterator.
Definition value.c:1495
void xmmsv_list_iter_first(xmmsv_list_iter_t *it)
Rewind the iterator to the start of the list.
Definition value.c:1523
void xmmsv_list_iter_explicit_destroy(xmmsv_list_iter_t *it)
Explicitly free list iterator.
Definition value.c:1478
int xmmsv_list_iter_valid(xmmsv_list_iter_t *it)
Check whether the iterator is valid and points to a valid element.
Definition value.c:1512
int xmmsv_list_iter_entry_int(xmmsv_list_iter_t *it, int32_t *val)
int xmmsv_list_get(xmmsv_t *listv, int pos, xmmsv_t **val)
Get the element at the given position in the list xmmsv_t.
Definition value.c:1218
int xmmsv_list_get_size(xmmsv_t *listv)
Return the size of the list.
Definition value.c:1403
xmms_mediainfo_reader_t * xmms_mediainfo_reader_start(void)
Start a new mediainfo reader thread.
Definition mediainfo.c:67
xmms_medialib_entry_t xmms_medialib_entry_new_encoded(xmms_medialib_session_t *session, const char *url, xmms_error_t *error)
Definition medialib.c:952
xmmsv_t * xmms_medialib_entry_property_get_value(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property)
Definition medialib.c:484
void xmms_medialib_add_recursive(xmms_medialib_t *medialib, const gchar *playlist, const gchar *path, xmms_error_t *error)
Definition medialib.c:860
xmms_medialib_t * xmms_medialib_init(xmms_playlist_t *playlist)
Initialize the medialib and open the database file.
Definition medialib.c:315
void xmms_medialib_insert_recursive(xmms_medialib_t *medialib, const gchar *playlist, gint32 pos, const gchar *path, xmms_error_t *error)
Definition medialib.c:873
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition medialib.c:425
gboolean check_string_list(xmmsv_t *list)
Checks that the list only contains string values.
Definition object.c:469
void xmms_object_connect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Connect to a signal that is emitted by this object.
Definition object.c:115
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
Definition object.c:256
xmms_mediainfo_reader_t * xmms_playlist_mediainfo_reader_get(xmms_playlist_t *playlist)
returns pointer to mediainfo reader.
Definition playlist.c:1547
void xmms_playlist_insert_entry(xmms_playlist_t *playlist, const gchar *plname, guint32 pos, xmms_medialib_entry_t file, xmms_error_t *err)
Insert an entry at a given position in the playlist without validating it.
Definition playlist.c:886
xmms_playlist_t * xmms_playlist_init(void)
Initializes a new xmms_playlist_t.
Definition playlist.c:277
gboolean xmms_playlist_advance(xmms_playlist_t *playlist)
Go to next song in playlist according to current playlist mode.
Definition playlist.c:376
xmms_medialib_entry_t xmms_playlist_current_entry(xmms_playlist_t *playlist)
Retrieve the currently active xmms_medialib_entry_t.
Definition playlist.c:394
void xmms_playlist_add_entry_unlocked(xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *plcoll, xmms_medialib_entry_t file, xmms_error_t *err)
Add an entry to the playlist without locking the mutex.
Definition playlist.c:1082
void xmms_playlist_add_entry(xmms_playlist_t *playlist, const gchar *plname, xmms_medialib_entry_t file, xmms_error_t *err)
Add an entry to the playlist without validating it.
Definition playlist.c:1062
gboolean xmms_playlist_remove_by_entry(xmms_playlist_t *playlist, xmms_medialib_entry_t entry)
Remove all additions of entry in the playlist.
Definition playlist.c:670
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed.
Definition value.c:303
int xmmsv_get_coll(const xmmsv_t *val, xmmsv_coll_t **coll)
Retrieves a collection from the value.
Definition value.c:883
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition value.c:180
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition value.c:863
struct xmmsv_St xmmsv_t
int xmmsv_get_int(const xmmsv_t *val, int32_t *r)
Retrieves a signed integer from the value.
Definition value.c:823
xmmsv_type_t xmmsv_get_type(const xmmsv_t *val)
Get the type of the value.
Definition value.c:392
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
Definition value.c:161
@ XMMSV_TYPE_DICT
@ XMMSV_TYPE_INT32
@ XMMSV_TYPE_STRING
gboolean xmms_medialib_check_id(xmms_medialib_entry_t entry)
Definition medialib.c:1402
#define XMMS_PLAYLIST_CHANGED_MSG(type, id, name)
Definition playlist.c:84
void xmms_playlist_changed_msg_send(xmms_playlist_t *playlist, GTree *dict)
Definition playlist.c:1686
GTree * xmms_playlist_changed_msg_new(xmms_playlist_t *playlist, xmms_playlist_changed_actions_t type, xmms_medialib_entry_t id, const gchar *plname)
Definition playlist.c:1645
#define XMMS_PLAYLIST_CURRPOS_MSG(pos, name)
Definition playlist.c:85
struct xmms_medialib_St xmms_medialib_t
struct xmms_config_property_St xmms_config_property_t
Definition xmms_config.h:26
#define XMMS_DBG(fmt,...)
Definition xmms_log.h:32
#define xmms_medialib_begin_write()
#define xmms_medialib_begin()
struct xmms_medialib_session_St xmms_medialib_session_t
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
#define XMMS_COLLECTION_PLAYLIST_CHANGED_MSG(dag, name)
@ XMMS_COLLECTION_NSID_PLAYLISTS
struct xmms_coll_dag_St xmms_coll_dag_t
#define xmms_error_isok(e)
Definition xmms_error.h:58
#define xmms_error_iserror(e)
Definition xmms_error.h:57
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
struct xmms_mediainfo_reader_St xmms_mediainfo_reader_t
#define xmms_object_new(objtype, destroyfunc)
#define XMMS_OBJECT(p)
Definition xmms_object.h:77
#define xmms_object_unref(obj)
#define XMMS_DEFAULT_PARTYSHUFFLE_UPCOMING
struct xmms_playlist_St xmms_playlist_t
@ XMMS_ERROR_GENERIC
@ XMMS_ERROR_NOENT
@ XMMS_ERROR_INVAL
#define XMMS_ACTIVE_PLAYLIST
@ XMMS_COLLECTION_TYPE_QUEUE
@ XMMS_COLLECTION_TYPE_PARTYSHUFFLE
xmms_playlist_changed_actions_t
@ XMMS_PLAYLIST_CHANGED_UPDATE
@ XMMS_PLAYLIST_CHANGED_MOVE
@ XMMS_PLAYLIST_CHANGED_REMOVE
@ XMMS_PLAYLIST_CHANGED_SHUFFLE
@ XMMS_PLAYLIST_CHANGED_ADD
@ XMMS_PLAYLIST_CHANGED_CLEAR
@ XMMS_PLAYLIST_CHANGED_INSERT
@ XMMS_PLAYLIST_CHANGED_SORT
@ XMMS_IPC_SIGNAL_PLAYLIST_CHANGED
@ XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS
@ XMMS_IPC_SIGNAL_PLAYLIST_LOADED
struct xmmsv_coll_St xmmsv_coll_t
Definition xmmsv_coll.h:28