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 
30 #include "xmmspriv/xmms_playlist.h"
31 #include "xmms/xmms_ipc.h"
32 #include "xmms/xmms_config.h"
33 #include "xmmspriv/xmms_medialib.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 */
44 static void xmms_playlist_destroy (xmms_object_t *object);
45 static void xmms_playlist_client_shuffle (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
46 static void xmms_playlist_client_clear (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
47 static void xmms_playlist_client_sort (xmms_playlist_t *playlist, const gchar *plname, xmmsv_t *property, xmms_error_t *err);
48 static GList * xmms_playlist_client_list_entries (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
49 static gchar *xmms_playlist_client_current_active (xmms_playlist_t *playlist, xmms_error_t *err);
50 static void xmms_playlist_destroy (xmms_object_t *object);
51 
52 static void xmms_playlist_client_add_id (xmms_playlist_t *playlist, const gchar *plname, xmms_medialib_entry_t file, xmms_error_t *error);
53 static void xmms_playlist_client_add_url (xmms_playlist_t *playlist, const gchar *plname, const gchar *nurl, xmms_error_t *err);
54 static void xmms_playlist_client_add_idlist (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll, xmms_error_t *err);
55 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);
56 static GTree * xmms_playlist_client_current_pos (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err);
57 static gint xmms_playlist_client_set_next (xmms_playlist_t *playlist, gint32 pos, xmms_error_t *error);
58 static void xmms_playlist_client_remove_entry (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, xmms_error_t *err);
59 static gboolean xmms_playlist_remove_unlocked (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *plcoll, guint pos, xmms_error_t *err);
60 static void xmms_playlist_client_move_entry (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, gint32 newpos, xmms_error_t *err);
61 static gint xmms_playlist_client_set_next_rel (xmms_playlist_t *playlist, gint32 pos, xmms_error_t *error);
62 static gint xmms_playlist_set_current_position_do (xmms_playlist_t *playlist, guint32 pos, xmms_error_t *err);
63 
64 static void xmms_playlist_client_insert_url (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, const gchar *url, xmms_error_t *error);
65 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);
66 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);
67 static void xmms_playlist_client_radd (xmms_playlist_t *playlist, const gchar *plname, const gchar *path, xmms_error_t *error);
68 static void xmms_playlist_client_rinsert (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, const gchar *path, xmms_error_t *error);
69 
70 static void xmms_playlist_client_load (xmms_playlist_t *, const gchar *, xmms_error_t *);
71 
72 static xmmsv_coll_t *xmms_playlist_get_coll (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *error);
73 static const gchar *xmms_playlist_canonical_name (xmms_playlist_t *playlist, const gchar *plname);
74 static gint xmms_playlist_coll_get_currpos (xmmsv_coll_t *plcoll);
75 static gint xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll);
76 
77 static void xmms_playlist_update_queue (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll);
78 static void xmms_playlist_update_partyshuffle (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll);
79 static void xmms_playlist_register_ipc_commands (xmms_object_t *playlist_object);
80 
81 static void xmms_playlist_current_pos_msg_send (xmms_playlist_t *playlist, GTree *dict);
82 static 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 */
98 struct 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 
117 static void
118 on_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 
131 static void
132 on_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 
146 static void
147 on_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 
177 static void
178 on_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 
184 static void
185 on_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 
204 static void
205 xmms_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 
225 static void
226 xmms_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 
313 static gboolean
314 xmms_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  */
375 gboolean
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  */
436 GTree *
437 xmms_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  */
471 static gchar *
472 xmms_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 
503 static void
504 xmms_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 
534  xmms_object_emit_f (XMMS_OBJECT (playlist),
537  name);
538 }
539 
540 static inline void
541 swap_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  */
557 static void
558 xmms_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 
603 static gboolean
604 xmms_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 
619  dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_REMOVE, 0, plname);
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 
635 typedef struct {
636  xmms_playlist_t *pls;
637  xmms_medialib_entry_t entry;
638 } playlist_remove_info_t;
639 
640 static void
641 remove_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  */
669 gboolean
671  xmms_medialib_entry_t entry)
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  */
694 void
695 xmms_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  */
717 static void
718 xmms_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  */
796 static void
797 xmms_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  */
824 static void
825 xmms_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  */
843 static void
844 xmms_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 
857 static void
858 xmms_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  */
885 void
886 xmms_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  */
940 void
941 xmms_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  */
966 static void
967 xmms_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 
989 void
990 xmms_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 
1002 void
1003 xmms_playlist_client_add_idlist (xmms_playlist_t *playlist,
1004  const gchar *plname,
1005  xmmsv_coll_t *coll, xmms_error_t *err)
1006 {
1007  xmms_medialib_entry_t entry;
1008  xmmsv_list_iter_t *it;
1009 
1011  for (xmmsv_list_iter_first (it);
1012  xmmsv_list_iter_valid (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);
1025  xmmsv_list_iter_valid (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 
1035 void
1036 xmms_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  */
1061 void
1062 xmms_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  */
1081 void
1083  const gchar *plname,
1084  xmmsv_coll_t *plcoll,
1085  xmms_medialib_entry_t file,
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 */
1101 static void
1102 xmms_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 
1133 static gint
1134 xmms_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 
1181 gint
1182 xmms_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 
1195 static gint
1196 xmms_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 
1240 typedef 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  */
1253 static gint
1254 xmms_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 &&
1291  xmmsv_get_type (val2) == 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 &&
1303  xmmsv_get_type (val2) == 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  */
1329 static void
1330 xmms_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  */
1348 static void
1349 xmms_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 
1375 static void
1376 xmms_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 */
1509 static GList *
1510 xmms_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;
1515  xmms_medialib_entry_t entry;
1516  xmmsv_list_iter_t *it;
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);
1530  xmmsv_list_iter_valid (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 
1561 static void
1562 xmms_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 
1583 static xmmsv_coll_t *
1584 xmms_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  */
1602 static const gchar *
1603 xmms_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). */
1622 static gint
1623 xmms_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). */
1637 static gint
1638 xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll)
1639 {
1640  return xmmsv_coll_idlist_get_size (plcoll);
1641 }
1642 
1643 
1644 GTree *
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 
1667 GTree *
1668 xmms_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 
1685 void
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) &&
1700  type != XMMS_PLAYLIST_CHANGED_UPDATE &&
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 
1713 static void
1714 xmms_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 xmmsv_list_iter_first(xmmsv_list_iter_t *it)
Rewind the iterator to the start of the list.
Definition: value.c:1523
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
#define xmms_error_isok(e)
Definition: xmms_error.h:58
xmms_mediainfo_reader_t * xmms_mediainfo_reader_start(void)
Start a new mediainfo reader thread.
Definition: mediainfo.c:67
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
struct xmmsv_list_iter_St xmmsv_list_iter_t
Definition: xmmsv_list.h:69
struct xmms_mediainfo_reader_St xmms_mediainfo_reader_t
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
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
#define XMMS_COLLECTION_PLAYLIST_CHANGED_MSG(dag, name)
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
void xmms_medialib_add_recursive(xmms_medialib_t *medialib, const gchar *playlist, const gchar *path, xmms_error_t *error)
Definition: medialib.c:860
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
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
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
int xmmsv_coll_idlist_append(xmmsv_coll_t *coll, int id)
Append a value to the idlist.
Definition: coll.c:252
#define xmms_error_iserror(e)
Definition: xmms_error.h:57
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
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_lookup(const gchar *path)
Look up a config key from the global config.
Definition: config.c:171
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
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition: value.c:180
xmms_playlist_changed_actions_t
int xmmsv_get_int(const xmmsv_t *val, int32_t *r)
Retrieves a signed integer from the value.
Definition: value.c:823
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
gboolean check_string_list(xmmsv_t *list)
Checks that the list only contains string values.
Definition: object.c:469
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
gboolean xmms_playlist_advance(xmms_playlist_t *playlist)
Go to next song in playlist according to current playlist mode.
Definition: playlist.c:376
struct xmmsv_St * xmmsv_coll_operands_get(xmmsv_coll_t *coll)
Definition: coll.c:437
size_t xmmsv_coll_idlist_get_size(xmmsv_coll_t *coll)
Get the size of the idlist.
Definition: coll.c:352
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
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
#define XMMS_DEFAULT_PARTYSHUFFLE_UPCOMING
Definition: xmms_playlist.h:37
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
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_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
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
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
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
void xmmsv_list_iter_next(xmmsv_list_iter_t *it)
Advance the iterator to the next element in the list.
Definition: value.c:1553
xmms_medialib_t * xmms_medialib_init(xmms_playlist_t *playlist)
Initialize the medialib and open the database file.
Definition: medialib.c:315
void xmmsv_list_iter_explicit_destroy(xmmsv_list_iter_t *it)
Explicitly free list iterator.
Definition: value.c:1478
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
struct xmmsv_coll_St xmmsv_coll_t
Definition: xmmsv_coll.h:28
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
gboolean xmms_medialib_check_id(xmms_medialib_entry_t entry)
Definition: medialib.c:1402
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
#define XMMS_PLAYLIST_CURRPOS_MSG(pos, name)
Definition: playlist.c:85
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
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
xmms_mediainfo_reader_t * xmms_playlist_mediainfo_reader_get(xmms_playlist_t *playlist)
returns pointer to mediainfo reader.
Definition: playlist.c:1547
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
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
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
int xmmsv_list_get_size(xmmsv_t *listv)
Return the size of the list.
Definition: value.c:1403
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
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
#define XMMS_ACTIVE_PLAYLIST
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
struct xmms_medialib_session_St xmms_medialib_session_t
Definition: xmms_medialib.h:87
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
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
void xmms_playlist_changed_msg_send(xmms_playlist_t *playlist, GTree *dict)
Definition: playlist.c:1686
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
xmms_playlist_t * xmms_playlist_init(void)
Initializes a new xmms_playlist_t.
Definition: playlist.c:277
#define xmms_medialib_begin_write()
#define xmms_medialib_begin()
struct xmms_medialib_St xmms_medialib_t
Definition: xmms_medialib.h:27
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
xmms_coll_dag_t * xmms_collection_init(xmms_playlist_t *playlist)
Initializes a new xmms_coll_dag_t.
Definition: collection.c:214
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_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:863
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.
Definition: collection.c:1373
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
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
struct xmms_config_property_St xmms_config_property_t
Definition: xmms_config.h:26
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
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
int xmmsv_list_iter_entry_int(xmmsv_list_iter_t *it, int32_t *val)
int xmmsv_coll_idlist_clear(xmmsv_coll_t *coll)
Empties the idlist.
Definition: coll.c:309
xmms_medialib_entry_t xmms_playlist_current_entry(xmms_playlist_t *playlist)
Retrieve the currently active xmms_medialib_entry_t.
Definition: playlist.c:394
xmmsv_coll_type_t xmmsv_coll_get_type(xmmsv_coll_t *coll)
Return the type of the collection.
Definition: coll.c:367
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition: medialib.c:425
int xmmsv_get_coll(const xmmsv_t *val, xmmsv_coll_t **coll)
Retrieves a collection from the value.
Definition: value.c:883
struct xmms_coll_dag_St xmms_coll_dag_t
#define XMMS_PLAYLIST_CHANGED_MSG(type, id, name)
Definition: playlist.c:84