XMMS2
collection.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 * Manages collections
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
34#include "xmmspriv/xmms_xform.h"
36#include "xmms/xmms_ipc.h"
37#include "xmms/xmms_config.h"
38#include "xmms/xmms_log.h"
39
40
41/* Internal helper structures */
42
43typedef struct {
44 const gchar *name;
45 const gchar *namespace;
46 xmmsv_coll_t *oldtarget;
47 xmmsv_coll_t *newtarget;
48} coll_rebind_infos_t;
49
50typedef struct {
51 const gchar* oldname;
52 const gchar* newname;
53 const gchar* namespace;
54} coll_rename_infos_t;
55
56typedef struct {
57 xmms_coll_dag_t *dag;
58 FuncApplyToColl func;
59 void *udata;
60} coll_call_infos_t;
61
62typedef struct {
63 const gchar *target_name;
64 const gchar *target_namespace;
65 gboolean found;
66} coll_refcheck_t;
67
68typedef struct {
69 const gchar *key;
70 xmmsv_coll_t *value;
71} coll_table_pair_t;
72
73typedef enum {
78
79typedef struct add_metadata_from_tree_user_data_St {
82 guint src;
84
85static GList *global_stream_type;
86
87/* Functions */
88
89static void xmms_collection_destroy (xmms_object_t *object);
90
91static gboolean xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
92static gboolean xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
93static gboolean xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid);
94
95static gboolean xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *tg_name, const gchar *tg_ns);
96
97static void xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, FuncApplyToColl f, void *udata);
98
99static void call_apply_to_coll (gpointer name, gpointer coll, gpointer udata);
100static void prepend_key_string (gpointer key, gpointer value, gpointer udata);
101static gboolean value_match_save_key (gpointer key, gpointer val, gpointer udata);
102
103static void rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
104static void rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
105static void strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
106static void check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
107
108static void coll_unref (void *coll);
109
110static GHashTable *xmms_collection_media_info (xmms_medialib_entry_t mid, xmms_error_t *err);
111
112static gboolean filter_get_mediainfo_field_string (xmmsv_coll_t *coll, GHashTable *mediainfo, gchar **val);
113static gboolean filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val);
114static gboolean filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val);
115static gboolean filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val);
116static gboolean filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val);
117
118static void build_match_table (gpointer key, gpointer value, gpointer udata);
119static gboolean find_unchecked (gpointer name, gpointer value, gpointer udata);
120static void build_list_matches (gpointer key, gpointer value, gpointer udata);
121
122static gboolean xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
123static gboolean xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
124static gboolean xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table, const gchar *refname, const gchar *refns);
125static gboolean xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
126static gboolean xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
127static gboolean xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
128static gboolean xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
129static gboolean xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
130
131static xmmsv_coll_t * xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
132static GList * xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace, xmms_error_t *error);
133static void xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace, xmmsv_coll_t *coll, xmms_error_t *error);
134static void xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
135static GList * xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace, xmms_error_t *error);
136static void xmms_collection_client_rename (xmms_coll_dag_t *dag, const gchar *from_name, const gchar *to_name, const gchar *namespace, xmms_error_t *error);
137
138static GList * xmms_collection_client_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err);
139static GList * xmms_collection_client_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmms_error_t *err);
140static xmmsv_coll_t *xmms_collection_client_idlist_from_playlist (xmms_coll_dag_t *dag, const gchar *mediainfo, xmms_error_t *err);
141static void xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err);
142
143
144#include "collection_ipc.c"
145
146GTree *
148 const gchar *plname, const gchar *namespace)
149{
150 GTree *dict;
151
152 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
153 NULL, (GDestroyNotify)xmmsv_unref);
154
155 g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type));
156 g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (plname));
157 g_tree_insert (dict, (gpointer) "namespace", xmmsv_new_string (namespace));
158
159 return dict;
160}
161
162void
164{
165 g_return_if_fail (colldag);
166 g_return_if_fail (dict);
167
171 dict);
172
173 g_tree_destroy (dict);
174}
175
176#define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace) xmms_collection_changed_msg_send (dag, xmms_collection_changed_msg_new (type, name, namespace))
177
178
179/** @defgroup Collection Collection
180 * @ingroup XMMSServer
181 * @brief This is the collection manager.
182 *
183 * The set of collections is stored as a DAG of collection operators.
184 * Each collection namespace contains a list of saved collections,
185 * with a pointer to the node in the graph.
186 * @{
187 */
188
189/** Collection DAG structure */
190
191struct xmms_coll_dag_St {
192 xmms_object_t object;
193
194 /* Ref to the playlist object, needed to notify it when a playlist changes */
195 xmms_playlist_t *playlist;
196
197 GHashTable *collrefs[XMMS_COLLECTION_NUM_NAMESPACES];
198
199 GMutex *mutex;
200
201};
202
203static void
204coll_sync_cb (xmms_object_t *object, xmmsv_t *val, gpointer udata)
205{
207}
208
209/** Initializes a new xmms_coll_dag_t.
210 *
211 * @returns The newly allocated collection DAG.
212 */
215{
216 gint i;
217 xmms_coll_dag_t *ret;
219
220 ret = xmms_object_new (xmms_coll_dag_t, xmms_collection_destroy);
221 ret->mutex = g_mutex_new ();
222 ret->playlist = playlist;
223
225
226 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
227 ret->collrefs[i] = g_hash_table_new_full (g_str_hash, g_str_equal,
228 g_free, coll_unref);
229 }
230
231 xmms_collection_register_ipc_commands (XMMS_OBJECT (ret));
232
233 /* Connection coll_sync_cb to some signals */
236 coll_sync_cb, ret);
237
238 /* FIXME: These signals should trigger COLLECTION_CHANGED */
241 coll_sync_cb, ret);
242
245 coll_sync_cb, ret);
246
249 coll_sync_cb, ret);
250
251
253
256 "application/x-xmms2-playlist-entries",
258 global_stream_type = g_list_prepend (NULL, f);
259
260 return ret;
261}
262
263static void
264add_metadata_from_tree (const gchar *key, xmmsv_t *value, gpointer user_data)
265{
267
268 if (xmmsv_get_type (value) == XMMSV_TYPE_INT32) {
269 gint iv;
270 xmmsv_get_int (value, &iv);
271 xmms_medialib_entry_property_set_int_source (ud->session, ud->entry,
272 key,
273 iv,
274 ud->src);
275 } else if (xmmsv_get_type (value) == XMMSV_TYPE_STRING) {
276 const gchar *sv;
277 xmmsv_get_string (value, &sv);
278 xmms_medialib_entry_property_set_str_source (ud->session, ud->entry,
279 key,
280 sv,
281 ud->src);
282 }
283}
284
285
286/** Create a idlist from a playlist file
287 * @param dag The collection DAG.
288 * @param path URL to the playlist file
289 * @param err If error occurs, a message is stored in this variable.
290 * @returns A idlist
291 */
292static xmmsv_coll_t *
293xmms_collection_client_idlist_from_playlist (xmms_coll_dag_t *dag,
294 const gchar *path,
295 xmms_error_t *err)
296{
297 xmms_xform_t *xform;
298 GList *lst, *n;
299 xmmsv_coll_t *coll;
301 guint src;
302 const gchar *buf;
303
304 /* we don't want any effects for playlist, so just report we're rehashing */
305 xform = xmms_xform_chain_setup_url (0, path, global_stream_type, TRUE);
306
307 if (!xform) {
308 xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "We can't handle this type of playlist or URL");
309 return NULL;
310 }
311
312 lst = xmms_xform_browse_method (xform, "/", err);
313 if (xmms_error_iserror (err)) {
314 xmms_object_unref (xform);
315 return NULL;
316 }
317
319 session = xmms_medialib_begin_write ();
320 src = xmms_medialib_source_to_id (session, "plugin/playlist");
321
322 n = lst;
323 while (n) {
325
326 xmmsv_t *a = n->data;
327 xmmsv_t *b;
328
329 if (!xmmsv_dict_get (a, "realpath", &b)) {
330 xmms_log_error ("Playlist plugin did not set realpath; probably a bug in plugin");
331 xmmsv_unref (a);
332 n = g_list_delete_link (n, n);
333 continue;
334 }
335
336 xmmsv_get_string (b, &buf);
337 entry = xmms_medialib_entry_new_encoded (session, buf, err);
338 xmmsv_dict_remove (a, "realpath");
339 xmmsv_dict_remove (a, "path");
340
341 if (entry) {
343 udata.session = session;
344 udata.entry = entry;
345 udata.src = src;
346
347 xmmsv_dict_foreach(a, add_metadata_from_tree, &udata);
348
349 xmmsv_coll_idlist_append (coll, entry);
350 } else {
351 xmmsv_get_string (b, &buf);
352 xmms_log_error ("couldn't add %s to collection!", buf);
353 }
354
355 xmmsv_unref (a);
356 n = g_list_delete_link (n, n);
357 }
358
359 xmms_medialib_end (session);
360 xmms_object_unref (xform);
361
362 return coll;
363}
364
365/** Remove the given collection from the DAG.
366*
367* If to be removed from ALL namespaces, then all matching collections are removed.
368*
369* @param dag The collection DAG.
370* @param name The name of the collection to remove.
371* @param namespace The namespace where the collection to remove is (can be ALL).
372* @param err If an error occurs, a message is stored in it.
373* @returns True on success, false otherwise.
374*/
375void
376xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *name,
377 const gchar *namespace, xmms_error_t *err)
378{
379 guint nsid;
380 gboolean retval = FALSE;
381 guint i;
382
383 nsid = xmms_collection_get_namespace_id (namespace);
384 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
385 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
386 return;
387 }
388
389 g_mutex_lock (dag->mutex);
390
391 /* Unreference the matching collection(s) */
392 if (nsid == XMMS_COLLECTION_NSID_ALL) {
393 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
394 retval = xmms_collection_unreference (dag, name, i) || retval;
395 }
396 } else {
397 retval = xmms_collection_unreference (dag, name, nsid);
398 }
399
400 g_mutex_unlock (dag->mutex);
401
402 if (retval == FALSE) {
403 xmms_error_set (err, XMMS_ERROR_NOENT, "Failed to remove this collection!");
404 }
405
406}
407
408/** Save the given collection in the DAG under the given name in the given namespace.
409 *
410 * @param dag The collection DAG in which to save the collection.
411 * @param name The name under which to save the collection.
412 * @param namespace The namespace in which to save th collection.
413 * @param coll The collection structure to save.
414 * @param err If an error occurs, a message is stored in it.
415 * @returns True on success, false otherwise.
416 */
417void
418xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace,
419 xmmsv_coll_t *coll, xmms_error_t *err)
420{
421 xmmsv_coll_t *existing;
422 guint nsid;
423 const gchar *alias;
424 gchar *newkey = NULL;
425
426 nsid = xmms_collection_get_namespace_id (namespace);
427 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
428 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
429 return;
430 } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
431 xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot save collection in all namespaces");
432 return;
433 }
434
435 /* Validate collection structure */
436 if (!xmms_collection_validate (dag, coll, name, namespace)) {
437 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
438 return;
439 }
440
441 g_mutex_lock (dag->mutex);
442
443 /* Unreference previously saved collection */
444 existing = xmms_collection_get_pointer (dag, name, nsid);
445 if (existing != NULL) {
446 /* Rebind reference pointers to the new collection */
447 coll_rebind_infos_t infos = { name, namespace, existing, coll };
448 xmms_collection_apply_to_all_collections (dag, rebind_references, &infos);
449 }
450
451 /* Link references in newly saved collection to actual operators */
453
454 /* Update existing collection in the table */
455 if (existing != NULL) {
456 while ((alias = xmms_collection_find_alias (dag, nsid,
457 existing, NULL)) != NULL) {
458 newkey = g_strdup (alias);
459
460 /* update all pairs pointing to the old coll */
461 xmms_collection_dag_replace (dag, nsid, newkey, coll);
462 xmmsv_coll_ref (coll);
463
465 newkey,
466 namespace);
467 }
468
469 /* Save new collection in the table */
470 } else {
471 newkey = g_strdup (name);
472 xmms_collection_dag_replace (dag, nsid, newkey, coll);
473 xmmsv_coll_ref (coll);
474
476 newkey,
477 namespace);
478 }
479
480 g_mutex_unlock (dag->mutex);
481
482 /* If updating a playlist, trigger PLAYLIST_CHANGED */
483 if (nsid == XMMS_COLLECTION_NSID_PLAYLISTS) {
484 XMMS_PLAYLIST_COLLECTION_CHANGED_MSG (dag->playlist, newkey);
485 }
486
487}
488
489
490/** Retrieve the structure of a given collection.
491 *
492 * If looking in ALL namespaces, only the collection first found is returned!
493 *
494 * @param dag The collection DAG.
495 * @param name The name of the collection to retrieve.
496 * @param namespace The namespace in which to look for the collection.
497 * @param err If an error occurs, a message is stored in it.
498 * @returns The collection structure if found, NULL otherwise.
499 */
501xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *name,
502 const gchar *namespace, xmms_error_t *err)
503{
504 xmmsv_coll_t *coll = NULL;
505 guint nsid;
506
507 nsid = xmms_collection_get_namespace_id (namespace);
508 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
509 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
510 return NULL;
511 }
512
513 g_mutex_lock (dag->mutex);
514
515 coll = xmms_collection_get_pointer (dag, name, nsid);
516
517 /* Not found! */
518 if (coll == NULL) {
519 xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
520
521 /* New reference, will be freed after being put in the return message */
522 } else {
523 xmmsv_coll_ref (coll);
524 }
525
526 g_mutex_unlock (dag->mutex);
527
528 return coll;
529}
530
531
532/** Synchronize collection data to the database (i.e. to disk).
533 *
534 * @param dag The collection DAG.
535 * @param err If an error occurs, a message is stored in it.
536 */
537
538void
540{
541 g_return_if_fail (dag);
542
543 g_mutex_lock (dag->mutex);
544
546
547 g_mutex_unlock (dag->mutex);
548}
549
550
551void
552xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err)
553{
555}
556
557
558/** Lists the collections in the given namespace.
559 *
560 * @param dag The collection DAG.
561 * @param namespace The namespace to list collections from (can be ALL).
562 * @param err If an error occurs, a message is stored in it.
563 * @returns A newly allocated GList with the list of collection names.
564 * Remember that it is only the LIST that is copied. Not the entries.
565 * The entries are however referenced, and must be unreffed!
566 */
567GList *
568xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace,
569 xmms_error_t *err)
570{
571 GList *r = NULL;
572 guint nsid;
573
574 nsid = xmms_collection_get_namespace_id (namespace);
575 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
576 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
577 return NULL;
578 }
579
580 g_mutex_lock (dag->mutex);
581
582 /* Get the list of collections in the given namespace */
583 xmms_collection_foreach_in_namespace (dag, nsid, prepend_key_string, &r);
584
585 g_mutex_unlock (dag->mutex);
586
587 return r;
588}
589
590
591/** Find all collections in the given namespace that contain a given media.
592 *
593 * @param dag The collection DAG.
594 * @param mid The id of the media.
595 * @param namespace The namespace in which to look for collections.
596 * @param err If an error occurs, a message is stored in it.
597 * @returns A newly allocated GList with the names of the matching collections.
598 */
599GList *
600xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace,
601 xmms_error_t *err)
602{
603 GHashTable *mediainfo;
604 GList *ret = NULL;
605 guint nsid;
606 gchar *open_name;
607 GHashTable *match_table;
608 xmmsv_coll_t *coll;
609
610 /* Verify namespace */
611 nsid = xmms_collection_get_namespace_id (namespace);
612 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
613 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
614 return NULL;
615 }
616 if (nsid == XMMS_COLLECTION_NSID_ALL) {
617 xmms_error_set (err, XMMS_ERROR_INVAL, "cannot search in all namespaces");
618 return NULL;
619 }
620
621 /* Prepare the match table of all collections for the given namespace */
622 match_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
623 xmms_collection_foreach_in_namespace (dag, nsid, build_match_table, match_table);
624
625 /* Get all infos for the given mid */
626 mediainfo = xmms_collection_media_info (mid, err);
627
628 /* While not all collections have been checked, check next */
629 while (g_hash_table_find (match_table, find_unchecked, &open_name) != NULL) {
630 coll_find_state_t *match = g_new (coll_find_state_t, 1);
631 coll = xmms_collection_get_pointer (dag, open_name, nsid);
632 if (xmms_collection_media_match (dag, mediainfo, coll, nsid, match_table)) {
634 } else {
636 }
637 g_hash_table_replace (match_table, g_strdup (open_name), match);
638 }
639
640 /* List matching collections */
641 g_hash_table_foreach (match_table, build_list_matches, &ret);
642 g_hash_table_destroy (match_table);
643
644 g_hash_table_destroy (mediainfo);
645
646 return ret;
647}
648
649
650/** Rename a collection in a given namespace.
651 *
652 * @param dag The collection DAG.
653 * @param from_name The name of the collection to rename.
654 * @param to_name The new name of the collection.
655 * @param namespace The namespace to consider (cannot be ALL).
656 * @param err If an error occurs, a message is stored in it.
657 * @return True if a collection was found and renamed.
658 */
659void
660xmms_collection_client_rename (xmms_coll_dag_t *dag, const gchar *from_name,
661 const gchar *to_name, const gchar *namespace,
662 xmms_error_t *err)
663{
664 gboolean retval;
665 guint nsid;
666 xmmsv_coll_t *from_coll, *to_coll;
667
668 nsid = xmms_collection_get_namespace_id (namespace);
669 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
670 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
671 return;
672 } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
673 xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot rename collection in all namespaces");
674 return;
675 }
676
677 g_mutex_lock (dag->mutex);
678
679 from_coll = xmms_collection_get_pointer (dag, from_name, nsid);
680 to_coll = xmms_collection_get_pointer (dag, to_name, nsid);
681
682 /* Input validation */
683 if (from_coll == NULL) {
684 xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
685 retval = FALSE;
686
687 } else if (to_coll != NULL) {
688 xmms_error_set (err, XMMS_ERROR_NOENT, "a collection already exists with the target name");
689 retval = FALSE;
690
691 /* Update collection name everywhere */
692 } else {
693 GTree *dict;
694
695 /* insert new pair in hashtable */
696 xmms_collection_dag_replace (dag, nsid, g_strdup (to_name), from_coll);
697 xmmsv_coll_ref (from_coll);
698
699 /* remove old pair from hashtable */
700 g_hash_table_remove (dag->collrefs[nsid], from_name);
701
702 /* update name in all reference operators */
703 coll_rename_infos_t infos = { from_name, to_name, namespace };
704 xmms_collection_apply_to_all_collections (dag, rename_references, &infos);
705
706 /* Send _RENAME signal */
708 from_name, namespace);
709 g_tree_insert (dict, (gpointer) "newname", xmmsv_new_string (to_name));
711
712 retval = TRUE;
713 }
714
715 g_mutex_unlock (dag->mutex);
716
717}
718
719
720/** Find the ids of the media matched by a collection.
721 *
722 * @param dag The collection DAG.
723 * @param coll The collection used to match media.
724 * @param lim_start The beginning index of the LIMIT statement (0 to disable).
725 * @param lim_len The number of entries of the LIMIT statement (0 to disable).
726 * @param order The list of properties to order by (empty to disable).
727 * @param err If an error occurs, a message is stored in it.
728 * @return A list of media ids.
729 */
730GList *
732 gint32 lim_start, gint32 lim_len, xmmsv_t *order,
733 xmms_error_t *err)
734{
735 GList *res, *n;
736 xmmsv_t *fetch, *group, *idval;
737
738 /* no grouping, fetch only id */
739 group = xmmsv_new_list ();
740 fetch = xmmsv_new_list ();
741 idval = xmmsv_new_string ("id");
742 xmmsv_list_append (fetch, idval);
743
744 res = xmms_collection_client_query_infos (dag, coll, lim_start, lim_len, order, fetch, group, err);
745
746 /* FIXME: get an uint list directly ! (we're getting ints here actually) */
747 for (n = res; n; n = n->next) {
749 xmmsv_t *id_val, *cmdval = n->data;
750
751 xmmsv_dict_get (cmdval, "id", &id_val);
752 xmmsv_get_int (id_val, &id);
753 n->data = xmmsv_new_int (id);
754
755 xmmsv_unref (cmdval);
756 }
757
758 xmmsv_unref (group);
759 xmmsv_unref (fetch);
760 xmmsv_unref (idval);
761
762 return res;
763}
764
765
766GList *
767xmms_collection_client_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
768 gint32 lim_start, gint32 lim_len, xmmsv_t *order,
769 xmms_error_t *err)
770{
771 return xmms_collection_query_ids (dag, coll, lim_start, lim_len, order, err);
772}
773/** Find the properties of the media matched by a collection.
774 *
775 * @param dag The collection DAG.
776 * @param coll The collection used to match media.
777 * @param lim_start The beginning index of the LIMIT statement (0 to disable).
778 * @param lim_len The number of entries of the LIMIT statement (0 to disable).
779 * @param order The list of properties to order by, prefix by '-' to invert (empty to disable).
780 * @param fetch The list of properties to be retrieved.
781 * @param group The list of properties to group by (empty to disable).
782 * @param err If an error occurs, a message is stored in it.
783 * @return A list of property dicts for each entry.
784 */
785GList *
786xmms_collection_client_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
787 gint32 lim_start, gint32 lim_len, xmmsv_t *order,
788 xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err)
789{
790 GList *res = NULL;
791 GString *query;
792
793 /* check that fetch is not empty */
794 if (xmmsv_list_get_size (fetch) == 0) {
795 xmms_error_set (err, XMMS_ERROR_INVAL, "fetch list must not be empty!");
796 return NULL;
797 }
798
799 /* check for invalid property strings */
800 if (!check_string_list (order)) {
801 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid order list!");
802 return NULL;
803 }
804 if (!check_string_list (fetch)) {
805 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid fetch list!");
806 return NULL;
807 }
808 if (!check_string_list (group)) {
809 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid group list!");
810 return NULL;
811 }
812
813 /* validate the collection to query */
814 if (!xmms_collection_validate (dag, coll, NULL, NULL)) {
815 if (err) {
816 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
817 }
818 return NULL;
819 }
820
821 g_mutex_lock (dag->mutex);
822
823 query = xmms_collection_get_query (dag, coll, lim_start, lim_len,
824 order, fetch, group);
825
826 g_mutex_unlock (dag->mutex);
827
828 XMMS_DBG ("COLLECTIONS: query_infos with %s", query->str);
829
830 /* Run the query */
832 res = xmms_medialib_select (session, query->str, err);
833 xmms_medialib_end (session);
834
835 g_string_free (query, TRUE);
836
837 return res;
838}
839
840/**
841 * Update a reference to point to a new collection.
842 *
843 * @param dag The collection DAG.
844 * @param name The name of the reference to update.
845 * @param nsid The namespace in which to locate the reference.
846 * @param newtarget The new collection pointed to by the reference.
847 */
848void
850 guint nsid, xmmsv_coll_t *newtarget)
851{
852 xmms_collection_dag_replace (dag, nsid, g_strdup (name), newtarget);
853 xmmsv_coll_ref (newtarget);
854}
855
856/** Update the DAG to update the value of the pair with the given key. */
857void
860 gchar *key, xmmsv_coll_t *newcoll)
861{
862 g_hash_table_replace (dag->collrefs[nsid], key, newcoll);
863}
864
865/** Find the collection structure corresponding to the given name in the given namespace.
866 *
867 * @param dag The collection DAG.
868 * @param collname The name of the collection to find.
869 * @param nsid The namespace id.
870 * @returns The collection structure if found, NULL otherwise.
871 */
873xmms_collection_get_pointer (xmms_coll_dag_t *dag, const gchar *collname,
874 guint nsid)
875{
876 gint i;
877 xmmsv_coll_t *coll = NULL;
878
879 if (nsid == XMMS_COLLECTION_NSID_ALL) {
880 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES && coll == NULL; ++i) {
881 coll = g_hash_table_lookup (dag->collrefs[i], collname);
882 }
883 } else {
884 coll = g_hash_table_lookup (dag->collrefs[nsid], collname);
885 }
886
887 return coll;
888}
889
890/** Extract an attribute from a collection as an integer.
891 *
892 * @param coll The collection to extract the attribute from.
893 * @param attrname The name of the attribute.
894 * @param val The integer value of the attribute will be saved in this pointer.
895 * @return TRUE if attribute correctly read, FALSE otherwise
896 */
897gboolean
898xmms_collection_get_int_attr (xmmsv_coll_t *coll, const gchar *attrname, gint *val)
899{
900 gboolean retval = FALSE;
901 gint buf;
902 gchar *str;
903 gchar *endptr;
904
905 if (xmmsv_coll_attribute_get (coll, attrname, &str)) {
906 buf = strtol (str, &endptr, 10);
907
908 /* Valid integer string */
909 if (*endptr == '\0') {
910 *val = buf;
911 retval = TRUE;
912 }
913 }
914
915 return retval;
916}
917
918/** Set the attribute of a collection as an integer.
919 *
920 * @param coll The collection in which to set the attribute.
921 * @param attrname The name of the attribute.
922 * @param newval The new value of the attribute.
923 * @return TRUE if attribute successfully saved, FALSE otherwise.
924 */
925gboolean
926xmms_collection_set_int_attr (xmmsv_coll_t *coll, const gchar *attrname,
927 gint newval)
928{
929 gboolean retval = FALSE;
930 gchar str[XMMS_MAX_INT_ATTRIBUTE_LEN + 1];
931 gint written;
932
933 written = g_snprintf (str, sizeof (str), "%d", newval);
934 if (written < XMMS_MAX_INT_ATTRIBUTE_LEN) {
935 xmmsv_coll_attribute_set (coll, attrname, str);
936 retval = TRUE;
937 }
938
939 return retval;
940}
941
942
943/**
944 * Reverse-search the list of collections in the given namespace to
945 * find the first pair whose value matches the argument. If key is
946 * not NULL, any pair with the same key will be ignored.
947 *
948 * @param dag The collection DAG.
949 * @param nsid The id of the namespace to consider.
950 * @param value The value of the pair to find.
951 * @param key If not NULL, ignore any pair with that key.
952 * @return The key of the found pair.
953 */
954const gchar *
956 xmmsv_coll_t *value, const gchar *key)
957{
958 const gchar *otherkey = NULL;
959 coll_table_pair_t search_pair = { key, value };
960
961 if (g_hash_table_find (dag->collrefs[nsid], value_match_save_key,
962 &search_pair) != NULL) {
963 otherkey = search_pair.key;
964 }
965
966 return otherkey;
967}
968
969
970/**
971 * Get a random media entry from the given collection.
972 *
973 * @param dag The collection DAG.
974 * @param source The collection to query.
975 * @return A random media from the source collection, or 0 if none found.
976 */
979{
980 GList *res;
981 xmms_medialib_entry_t mid = 0;
982 xmmsv_t *rorder = xmmsv_new_list ();
983 xmmsv_t *randval = xmmsv_new_string ("~RANDOM()");
984
985 /* FIXME: Temporary hack to allow custom ordering functions */
986 xmmsv_list_append (rorder, randval);
987
988 res = xmms_collection_query_ids (dag, source, 0, 1, rorder, NULL);
989
990 if (res != NULL) {
991 xmmsv_t *val = (xmmsv_t *) res->data;
992 xmmsv_get_int (val, &mid);
993 xmmsv_unref (val);
994 g_list_free (res);
995 }
996
997 xmmsv_unref (rorder);
998 xmmsv_unref (randval);
999
1000 return mid;
1001}
1002
1003/** @} */
1004
1005
1006
1007/** Free the collection DAG and other memory in the xmms_coll_dag_t
1008 *
1009 * This will free all collections in the DAG!
1010 */
1011static void
1012xmms_collection_destroy (xmms_object_t *object)
1013{
1014 gint i;
1015 xmms_coll_dag_t *dag = (xmms_coll_dag_t *)object;
1016
1017 g_return_if_fail (dag);
1018
1021
1022 g_mutex_free (dag->mutex);
1023
1024 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1025 g_hash_table_destroy (dag->collrefs[i]); /* dag is freed here */
1026 }
1027
1028 xmms_collection_unregister_ipc_commands ();
1029}
1030
1031/** Validate the given collection against a DAG.
1032 *
1033 * @param dag The collection DAG.
1034 * @param coll The collection to validate.
1035 * @param save_name The name under which the collection will be saved (NULL
1036 * if none).
1037 * @param save_namespace The namespace in which the collection will be
1038 * saved (NULL if none).
1039 * @returns True if the collection is valid, false otherwise.
1040 */
1041static gboolean
1042xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1043 const gchar *save_name, const gchar *save_namespace)
1044{
1045 /* Special validation checks for the Playlists namespace */
1046 if (save_namespace != NULL &&
1047 strcmp (save_namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
1048 /* only accept idlists */
1052 return FALSE;
1053 }
1054 }
1055
1056 /* Standard checking of the whole coll DAG */
1057 return xmms_collection_validate_recurs (dag, coll, save_name,
1058 save_namespace);
1059}
1060
1061/**
1062 * Internal recursive validation function used to validate the whole
1063 * graph of a collection.
1064 */
1065static gboolean
1066xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1067 const gchar *save_name, const gchar *save_namespace)
1068{
1069 guint num_operands = 0;
1070 xmmsv_coll_t *op, *ref;
1071 gchar *attr, *attr2;
1072 gboolean valid = TRUE;
1073 xmmsv_coll_type_t type;
1075
1076 /* count operands */
1077 num_operands = xmmsv_list_get_size (xmmsv_coll_operands_get (coll));
1078
1079 /* analyse by type */
1080 type = xmmsv_coll_get_type (coll);
1081 switch (type) {
1083 /* zero or one (bound in DAG) operand */
1084 if (num_operands > 1) {
1085 return FALSE;
1086 }
1087
1088 /* check if referenced collection exists */
1089 xmmsv_coll_attribute_get (coll, "reference", &attr);
1090 if (attr == NULL) {
1091 return FALSE;
1092 } else if (strcmp (attr, "All Media") != 0) {
1093 xmmsv_coll_attribute_get (coll, "namespace", &attr2);
1094
1095 if (attr2 == NULL) {
1096 return FALSE;
1097 }
1098
1099 nsid = xmms_collection_get_namespace_id (attr2);
1100 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
1101 return FALSE;
1102 }
1103
1104 g_mutex_lock (dag->mutex);
1105 ref = xmms_collection_get_pointer (dag, attr, nsid);
1106 if (ref == NULL) {
1107 g_mutex_unlock (dag->mutex);
1108 return FALSE;
1109 }
1110
1111 if (save_name && save_namespace) {
1112 /* self-reference is of course forbidden */
1113 if (strcmp (attr, save_name) == 0 &&
1114 strcmp (attr2, save_namespace) == 0) {
1115
1116 g_mutex_unlock (dag->mutex);
1117 return FALSE;
1118
1119 /* check if the referenced coll references this one (loop!) */
1120 } else if (xmms_collection_has_reference_to (dag, ref, save_name,
1121 save_namespace)) {
1122 g_mutex_unlock (dag->mutex);
1123 return FALSE;
1124 }
1125 }
1126
1127 g_mutex_unlock (dag->mutex);
1128 } else {
1129 /* "All Media" reference, so no referenced coll pointer */
1130 ref = NULL;
1131 }
1132
1133 /* ensure that the operand is consistent with the reference infos */
1134 if (num_operands == 1) {
1135 xmmsv_t *val;
1136 xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &val);
1137 xmmsv_get_coll (val, &op);
1138
1139 if (op != ref) {
1140 return FALSE;
1141 }
1142 }
1143 break;
1144
1147 /* need operand(s) */
1148 if (num_operands == 0) {
1149 return FALSE;
1150 }
1151 break;
1152
1154 /* one operand */
1155 if (num_operands != 1) {
1156 return FALSE;
1157 }
1158 break;
1159
1161 /* one operand */
1162 if (num_operands != 1) {
1163 return FALSE;
1164 }
1165
1166 /* "field" attribute */
1167 /* with valid value */
1168 if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
1169 return FALSE;
1170 }
1171 break;
1172
1177 /* one operand */
1178 if (num_operands != 1) {
1179 return FALSE;
1180 }
1181
1182 /* "field"/"value" attributes */
1183 /* with valid values */
1184 if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
1185 return FALSE;
1186 }
1187 /* FIXME: valid fields?
1188 else if (...) {
1189 return FALSE;
1190 }
1191 */
1192
1193 if (!xmmsv_coll_attribute_get (coll, "value", &attr)) {
1194 return FALSE;
1195 }
1196 break;
1197
1200 /* no operand */
1201 if (num_operands > 0) {
1202 return FALSE;
1203 }
1204 break;
1205
1207 /* one operand */
1208 if (num_operands != 1) {
1209 return FALSE;
1210 }
1211 break;
1212
1213 /* invalid type */
1214 default:
1215 return FALSE;
1216 break;
1217 }
1218
1219
1220 /* recurse in operands */
1221 if (num_operands > 0 && type != XMMS_COLLECTION_TYPE_REFERENCE) {
1222 xmmsv_list_iter_t *iter;
1224
1225 for (xmmsv_list_iter_first (iter);
1226 valid && xmmsv_list_iter_valid (iter);
1227 xmmsv_list_iter_next (iter)) {
1228
1229 xmmsv_t *val;
1230 xmmsv_list_iter_entry (iter, &val);
1231 xmmsv_get_coll (val, &op);
1232
1233 if (!xmms_collection_validate_recurs (dag, op, save_name,
1234 save_namespace)) {
1235 valid = FALSE;
1236 }
1237 }
1238
1240 }
1241
1242 return valid;
1243}
1244
1245/** Try to unreference a collection from a given namespace.
1246 *
1247 * @param dag The collection DAG.
1248 * @param name The name of the collection to remove.
1249 * @param nsid The namespace in which to look for the collection (yes, redundant).
1250 * @returns TRUE if a collection was removed, FALSE otherwise.
1251 */
1252static gboolean
1253xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid)
1254{
1255 xmmsv_coll_t *existing, *active_pl;
1256 gboolean retval = FALSE;
1257
1258 existing = g_hash_table_lookup (dag->collrefs[nsid], name);
1259 active_pl = g_hash_table_lookup (dag->collrefs[XMMS_COLLECTION_NSID_PLAYLISTS],
1261
1262 /* Unref if collection exists, and is not pointed at by _active playlist */
1263 if (existing != NULL && existing != active_pl) {
1264 const gchar *matchkey;
1265 const gchar *nsname = xmms_collection_get_namespace_string (nsid);
1266 coll_rebind_infos_t infos = { name, nsname, existing, NULL };
1267
1268 /* FIXME: if reference pointed to by a label, we should update
1269 * the label to point to the ref'd operator instead ! */
1270
1271 /* Strip all references to the deleted coll, bind operator directly */
1272 xmms_collection_apply_to_all_collections (dag, strip_references, &infos);
1273
1274 /* Remove all pairs pointing to that collection */
1275 while ((matchkey = xmms_collection_find_alias (dag, nsid,
1276 existing, NULL)) != NULL) {
1277
1279 matchkey,
1280 nsname);
1281
1282 g_hash_table_remove (dag->collrefs[nsid], matchkey);
1283 }
1284
1285 retval = TRUE;
1286 }
1287
1288 return retval;
1289}
1290
1291/** Find the namespace id corresponding to a namespace string.
1292 *
1293 * @param namespace The namespace string.
1294 * @returns The namespace id.
1295 */
1297xmms_collection_get_namespace_id (const gchar *namespace)
1298{
1299 guint nsid;
1300
1301 if (strcmp (namespace, XMMS_COLLECTION_NS_ALL) == 0) {
1303 } else if (strcmp (namespace, XMMS_COLLECTION_NS_COLLECTIONS) == 0) {
1305 } else if (strcmp (namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
1307 } else {
1309 }
1310
1311 return nsid;
1312}
1313
1314/** Find the namespace name (string) corresponding to a namespace id.
1315 *
1316 * @param nsid The namespace id.
1317 * @returns The namespace name (string).
1318 */
1319const gchar *
1321{
1322 const gchar *name;
1323
1324 switch (nsid) {
1327 break;
1330 break;
1333 break;
1334
1336 default:
1337 name = NULL;
1338 break;
1339 }
1340
1341 return name;
1342}
1343
1344
1345/** Check whether a collection structure contains a reference to a given collection.
1346 *
1347 * @param dag The collection DAG.
1348 * @param coll The collection to inspect for reference.
1349 * @param tg_name The name of the collection to find a reference to.
1350 * @param tg_ns The namespace of the collection to find a reference to.
1351 * @returns True if the collection contains a reference to the given
1352 * collection, false otherwise
1353 */
1354static gboolean
1355xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1356 const gchar *tg_name, const gchar *tg_ns)
1357{
1358 coll_refcheck_t check = { tg_name, tg_ns, FALSE };
1359 xmms_collection_apply_to_collection (dag, coll, check_for_reference, &check);
1360
1361 return check.found;
1362}
1363
1364
1365/** Apply a function to all the collections in a given namespace.
1366 *
1367 * @param dag The collection DAG.
1368 * @param nsid The namespace id.
1369 * @param f The function to apply to all the collections.
1370 * @param udata Additional user data parameter passed to the function.
1371 */
1372void
1373xmms_collection_foreach_in_namespace (xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
1374{
1375 gint i;
1376
1377 if (nsid == XMMS_COLLECTION_NSID_ALL) {
1378 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1379 g_hash_table_foreach (dag->collrefs[i], f, udata);
1380 }
1381 } else if (nsid != XMMS_COLLECTION_NSID_INVALID) {
1382 g_hash_table_foreach (dag->collrefs[nsid], f, udata);
1383 }
1384}
1385
1386/** Apply a function of type #FuncApplyToColl to all the collections in all namespaces.
1387 *
1388 * @param dag The collection DAG.
1389 * @param f The function to apply to all the collections.
1390 * @param udata Additional user data parameter passed to the function.
1391 */
1392void
1394 FuncApplyToColl f, void *udata)
1395{
1396 gint i;
1397 coll_call_infos_t callinfos = { dag, f, udata };
1398
1399 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1400 g_hash_table_foreach (dag->collrefs[i], call_apply_to_coll, &callinfos);
1401 }
1402}
1403
1404/** Apply a function of type #FuncApplyToColl to the given collection.
1405 *
1406 * @param dag The collection DAG.
1407 * @param coll The collection on which to apply the function.
1408 * @param f The function to apply to all the collections.
1409 * @param udata Additional user data parameter passed to the function.
1410 */
1411void
1413 xmmsv_coll_t *coll,
1414 FuncApplyToColl f, void *udata)
1415{
1416 xmms_collection_apply_to_collection_recurs (dag, coll, NULL, f, udata);
1417}
1418
1419/* Internal function used for recursion (parent param, NULL by default) */
1420static void
1421xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag,
1422 xmmsv_coll_t *coll,
1423 xmmsv_coll_t *parent,
1424 FuncApplyToColl f, void *udata)
1425{
1426 xmmsv_coll_t *op;
1427
1428 /* Apply the function to the operator. */
1429 f (dag, coll, parent, udata);
1430
1431 /* Recurse into the operands (if not a reference) */
1433 xmmsv_list_iter_t *iter;
1435
1436 for (xmmsv_list_iter_first (iter);
1437 xmmsv_list_iter_valid (iter);
1438 xmmsv_list_iter_next (iter)) {
1439
1440 xmmsv_t *val;
1441 xmmsv_list_iter_entry (iter, &val);
1442
1443 xmmsv_get_coll (val, &op);
1444
1445 xmms_collection_apply_to_collection_recurs (dag, op, coll, f,
1446 udata);
1447 }
1448
1450 }
1451}
1452
1453
1454/**
1455 * Work-around function to call a function on the value of the pair.
1456 */
1457static void
1458call_apply_to_coll (gpointer name, gpointer coll, gpointer udata)
1459{
1460 coll_call_infos_t *callinfos = (coll_call_infos_t*)udata;
1461
1462 xmms_collection_apply_to_collection (callinfos->dag, coll,
1463 callinfos->func, callinfos->udata);
1464}
1465
1466/**
1467 * Prepend the key string (name) to the udata list.
1468 */
1469static void
1470prepend_key_string (gpointer key, gpointer value, gpointer udata)
1471{
1472 GList **list = (GList**)udata;
1473 *list = g_list_prepend (*list, xmmsv_new_string (key));
1474}
1475
1476/**
1477 * Returns TRUE if the value of the pair is equal to the value stored
1478 * in the udata structure, and save the corresponding key in that
1479 * structure.
1480 */
1481static gboolean
1482value_match_save_key (gpointer key, gpointer val, gpointer udata)
1483{
1484 gboolean found = FALSE;
1485 coll_table_pair_t *pair = (coll_table_pair_t*)udata;
1486 xmmsv_coll_t *coll = (xmmsv_coll_t*)val;
1487
1488 /* value matching and key not ignored, found! */
1489 if ((coll == pair->value) &&
1490 (pair->key == NULL || strcmp (pair->key, key) != 0)) {
1491 pair->key = key;
1492 found = TRUE;
1493 }
1494
1495 return found;
1496}
1497
1498/**
1499 * If a reference, add the operator of the pointed collection as an
1500 * operand.
1501 */
1502void
1504{
1506 xmmsv_coll_t *target;
1507 gchar *target_name;
1508 gchar *target_namespace;
1509 gint target_nsid;
1510
1511 xmmsv_coll_attribute_get (coll, "reference", &target_name);
1512 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1513 if (target_name == NULL || target_namespace == NULL ||
1514 strcmp (target_name, "All Media") == 0) {
1515 return;
1516 }
1517
1518 target_nsid = xmms_collection_get_namespace_id (target_namespace);
1519 if (target_nsid == XMMS_COLLECTION_NSID_INVALID) {
1520 return;
1521 }
1522
1523 target = xmms_collection_get_pointer (dag, target_name, target_nsid);
1524 if (target == NULL) {
1525 return;
1526 }
1527
1528 xmmsv_coll_add_operand (coll, target);
1529 }
1530}
1531
1532/**
1533 * If a reference, rebind the given operator to the new operator
1534 * representing the referenced collection (pointers and so are in the
1535 * udata structure).
1536 */
1537static void
1538rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1539{
1541 coll_rebind_infos_t *infos;
1542
1543 gchar *target_name = NULL;
1544 gchar *target_namespace = NULL;
1545
1546 infos = (coll_rebind_infos_t*)udata;
1547
1548 /* FIXME: Or only compare operand vs oldtarget ? */
1549
1550 xmmsv_coll_attribute_get (coll, "reference", &target_name);
1551 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1552 if (strcmp (infos->name, target_name) != 0 ||
1553 strcmp (infos->namespace, target_namespace) != 0) {
1554 return;
1555 }
1556
1557 xmmsv_coll_remove_operand (coll, infos->oldtarget);
1558 xmmsv_coll_add_operand (coll, infos->newtarget);
1559 }
1560}
1561
1562/**
1563 * If a reference with matching name, rename it according to the
1564 * rename infos in the udata structure.
1565 */
1566static void
1567rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1568{
1570 coll_rename_infos_t *infos;
1571
1572 gchar *target_name = NULL;
1573 gchar *target_namespace = NULL;
1574
1575 infos = (coll_rename_infos_t*)udata;
1576
1577 xmmsv_coll_attribute_get (coll, "reference", &target_name);
1578 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1579 if (strcmp (infos->oldname, target_name) == 0 &&
1580 strcmp (infos->namespace, target_namespace) == 0) {
1581 xmmsv_coll_attribute_set (coll, "reference", infos->newname);
1582 }
1583 }
1584}
1585
1586/**
1587 * Strip reference operators to the given collection by rebinding the
1588 * parent directly to the pointed operator.
1589 */
1590static void
1591strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1592{
1593 xmmsv_coll_t *op;
1594 coll_rebind_infos_t *infos;
1595 gchar *target_name = NULL;
1596 gchar *target_namespace = NULL;
1597 xmmsv_list_iter_t *iter;
1598 xmmsv_t *tmp;
1599
1600 infos = (coll_rebind_infos_t*)udata;
1601
1603 for (xmmsv_list_iter_first (iter);
1604 xmmsv_list_iter_valid (iter);
1605 xmmsv_list_iter_next (iter)) {
1606
1607 xmmsv_list_iter_entry (iter, &tmp);
1608 xmmsv_get_coll (tmp, &op);
1609
1610 /* Skip if not potential reference */
1612 continue;
1613 }
1614
1615 xmmsv_coll_attribute_get (op, "reference", &target_name);
1616 xmmsv_coll_attribute_get (op, "namespace", &target_namespace);
1617 if (strcmp (infos->name, target_name) != 0 ||
1618 strcmp (infos->namespace, target_namespace) != 0) {
1619 continue;
1620 }
1621
1622 /* Rebind coll to ref'd operand directly, effectively strip reference */
1623 /* FIXME: Do we really need to do this _clear? */
1625
1627
1628 tmp = xmmsv_new_coll (infos->oldtarget);
1629 xmmsv_list_iter_insert (iter, tmp);
1630 xmmsv_unref (tmp);
1631 }
1633}
1634
1635/**
1636 * Check if the current operator is a reference to a given collection,
1637 * and if so, update the structure passed as userdata.
1638 */
1639static void
1640check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1641{
1642 coll_refcheck_t *check = (coll_refcheck_t*)udata;
1643 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE && !check->found) {
1644 gchar *target_name, *target_namespace;
1645
1646 xmmsv_coll_attribute_get (coll, "reference", &target_name);
1647 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1648 if (strcmp (check->target_name, target_name) == 0 &&
1649 strcmp (check->target_namespace, target_namespace) == 0) {
1650 check->found = TRUE;
1651 } else {
1652 xmmsv_coll_t *op;
1653 xmmsv_t *tmp;
1654
1655 if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
1656 xmmsv_get_coll (tmp, &op);
1657 xmms_collection_apply_to_collection_recurs (dag, op, coll,
1658 check_for_reference,
1659 udata);
1660 }
1661 }
1662 }
1663}
1664
1665
1666/** Forwarding function to fix type warnings.
1667 *
1668 * @param coll The collection to unref.
1669 */
1670static void
1671coll_unref (void *coll)
1672{
1673 xmmsv_coll_unref (coll);
1674}
1675
1676
1677
1678/* ============ FIND / COLLECTION MATCH FUNCTIONS ============ */
1679
1680/* Generate a build_match hashtable, states initialized to UNCHECKED. */
1681static void
1682build_match_table (gpointer key, gpointer value, gpointer udata)
1683{
1684 GHashTable *match_table = udata;
1685 coll_find_state_t *match = g_new (coll_find_state_t, 1);
1687 g_hash_table_replace (match_table, g_strdup (key), match);
1688}
1689
1690/* Return the first unchecked element from the match_table, set the
1691 * udata pointer to contain the key of that element.
1692 */
1693static gboolean
1694find_unchecked (gpointer name, gpointer value, gpointer udata)
1695{
1696 coll_find_state_t *match = value;
1697 gchar **open = udata;
1698 *open = name;
1699 return (*match == XMMS_COLLECTION_FIND_STATE_UNCHECKED);
1700}
1701
1702/* Build a list of all matched entries of the match_table in the udata
1703 * pointer.
1704 */
1705static void
1706build_list_matches (gpointer key, gpointer value, gpointer udata)
1707{
1708 gchar *coll_name = key;
1709 coll_find_state_t *state = value;
1710 GList **list = udata;
1711 if (*state == XMMS_COLLECTION_FIND_STATE_MATCH) {
1712 *list = g_list_prepend (*list, xmmsv_new_string (coll_name));
1713 }
1714}
1715
1716/** Determine whether the mediainfos match the given collection.
1717 *
1718 * @param dag The collection DAG.
1719 * @param mediainfo The properties of the media to match against.
1720 * @param coll The collection to match with the mediainfos.
1721 * @param nsid The namespace id of the collection.
1722 * @param match_table The match_table for all collections in that namespace.
1723 * @return TRUE if the collection matches, FALSE otherwise.
1724 */
1725static gboolean
1726xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1727 xmmsv_coll_t *coll, guint nsid,
1728 GHashTable *match_table)
1729{
1730 gboolean match = FALSE;
1731 xmmsv_coll_t *op;
1732 gchar *attr1 = NULL, *attr2 = NULL;
1733 xmmsv_t *val;
1734 xmms_medialib_entry_t entry, id;
1735 xmmsv_list_iter_t *iter;
1736
1737 switch (xmmsv_coll_get_type (coll)) {
1739 if (xmmsv_coll_attribute_get (coll, "reference", &attr1)) {
1740 if (strcmp (attr1, "All Media") == 0) {
1741 match = TRUE;
1742 } else if (xmmsv_coll_attribute_get (coll, "namespace", &attr2)) {
1743 match = xmms_collection_media_match_reference (dag, mediainfo,
1744 coll, nsid,
1745 match_table,
1746 attr1, attr2);
1747 }
1748 }
1749 break;
1750
1752 /* if ANY matches */
1754
1755 for (xmmsv_list_iter_first (iter);
1756 !match && xmmsv_list_iter_valid (iter);
1757 xmmsv_list_iter_next (iter)) {
1758
1759 xmmsv_list_iter_entry (iter, &val);
1760 xmmsv_get_coll (val, &op);
1761
1762 match = xmms_collection_media_match (dag, mediainfo, op,
1763 nsid, match_table);
1764 }
1766 break;
1767
1769 /* if ALL match */
1770 match = TRUE;
1772
1773 for (xmmsv_list_iter_first (iter);
1774 match && xmmsv_list_iter_valid (iter);
1775 xmmsv_list_iter_next (iter)) {
1776
1777 xmmsv_list_iter_entry (iter, &val);
1778 xmmsv_get_coll (val, &op);
1779
1780 match = xmms_collection_media_match (dag, mediainfo, op,
1781 nsid, match_table);
1782 }
1784 break;
1785
1787 /* invert result from operand */
1788 match = !xmms_collection_media_match_operand (dag, mediainfo, coll,
1789 nsid, match_table);
1790 break;
1791
1793 match = xmms_collection_media_filter_has (dag, mediainfo, coll,
1794 nsid, match_table);
1795 break;
1796
1798 match = xmms_collection_media_filter_equals (dag, mediainfo, coll,
1799 nsid, match_table);
1800 break;
1801
1803 match = xmms_collection_media_filter_match (dag, mediainfo, coll,
1804 nsid, match_table);
1805 break;
1806
1808 match = xmms_collection_media_filter_smaller (dag, mediainfo, coll,
1809 nsid, match_table);
1810 break;
1811
1813 match = xmms_collection_media_filter_greater (dag, mediainfo, coll,
1814 nsid, match_table);
1815 break;
1816
1820 /* check if id in idlist */
1821 val = g_hash_table_lookup (mediainfo, "id");
1822 if (val != NULL) {
1823 xmmsv_get_int (val, &id);
1824
1826 for (xmmsv_list_iter_first (iter);
1827 xmmsv_list_iter_valid (iter);
1828 xmmsv_list_iter_next (iter)) {
1829
1830 xmmsv_list_iter_entry_int (iter, &entry);
1831 if (entry == id) {
1832 match = TRUE;
1833 break;
1834 }
1835 }
1837 }
1838 break;
1839
1840 /* invalid type */
1841 default:
1842 XMMS_DBG ("invalid collection operator in xmms_collection_media_match");
1843 g_assert_not_reached ();
1844 break;
1845 }
1846
1847 return match;
1848}
1849
1850/** Determine whether the mediainfos match the given reference operator.
1851 *
1852 * @param dag The collection DAG.
1853 * @param mediainfo The properties of the media to match against.
1854 * @param coll The collection (ref op) to match with the mediainfos.
1855 * @param nsid The namespace id of the collection.
1856 * @param match_table The match_table for all collections in that namespace.
1857 * @param refname The name of the referenced collection.
1858 * @param refns The namespace of the referenced collection.
1859 * @return TRUE if the collection matches, FALSE otherwise.
1860 */
1861static gboolean
1862xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1863 xmmsv_coll_t *coll, guint nsid,
1864 GHashTable *match_table,
1865 const gchar *refname, const gchar *refns)
1866{
1867 gboolean match;
1868 guint refnsid;
1869 coll_find_state_t *matchstate;
1870
1871 /* Same NS, should be in the match table */
1872 refnsid = xmms_collection_get_namespace_id (refns);
1873 if (refnsid == nsid) {
1874 matchstate = g_hash_table_lookup (match_table, refname);
1875 if (*matchstate == XMMS_COLLECTION_FIND_STATE_UNCHECKED) {
1876 /* Check ref'd collection match status and save it */
1877 matchstate = g_new (coll_find_state_t, 1);
1878 match = xmms_collection_media_match_operand (dag,
1879 mediainfo,
1880 coll, nsid,
1881 match_table);
1882
1883 if (match) {
1885 } else {
1887 }
1888
1889 g_hash_table_replace (match_table, g_strdup (refname), matchstate);
1890
1891 } else {
1892 match = (*matchstate == XMMS_COLLECTION_FIND_STATE_MATCH);
1893 }
1894
1895 /* In another NS, just check if it matches */
1896 } else {
1897 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
1898 nsid, match_table);
1899 }
1900
1901 return match;
1902}
1903
1904/** Determine whether the mediainfos match the first operand of the
1905 * given operator.
1906 *
1907 * @param dag The collection DAG.
1908 * @param mediainfo The properties of the media to match against.
1909 * @param coll Match the mediainfos with the operand of that collection.
1910 * @param nsid The namespace id of the collection.
1911 * @param match_table The match_table for all collections in that namespace.
1912 * @return TRUE if the collection matches, FALSE otherwise.
1913 */
1914static gboolean
1915xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1916 xmmsv_coll_t *coll, guint nsid,
1917 GHashTable *match_table)
1918{
1919 xmmsv_coll_t *op;
1920 xmmsv_t *tmp;
1921 gboolean match = FALSE;
1922
1923 if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
1924 xmmsv_get_coll (tmp, &op);
1925
1926 match = xmms_collection_media_match (dag, mediainfo, op, nsid, match_table);
1927 }
1928
1929 return match;
1930}
1931
1932/** Get all the properties for the given media.
1933 *
1934 * @param mid The id of the media.
1935 * @return A HashTable with all the properties.
1936 */
1937static GHashTable *
1938xmms_collection_media_info (xmms_medialib_entry_t mid, xmms_error_t *err)
1939{
1940 GList *res;
1941 GList *n;
1942 GHashTable *infos;
1943 gchar *name;
1944 const gchar *buf;
1945 xmmsv_t *cmdval;
1946 xmmsv_t *value;
1947 guint state;
1948
1949 /* FIXME: could probably reuse tree from medialib_info directly. ignores sources? */
1950 res = xmms_medialib_info_list (NULL, mid, err);
1951
1952 /* Transform the list into a HashMap */
1953 infos = g_hash_table_new_full (g_str_hash, g_str_equal,
1954 g_free, (GDestroyNotify) xmmsv_unref);
1955 for (state = 0, n = res; n; state = (state + 1) % 3, n = n->next) {
1956 switch (state) {
1957 case 0: /* source */
1958 break;
1959
1960 case 1: /* prop name */
1961 cmdval = n->data;
1962 xmmsv_get_string (cmdval, &buf);
1963 name = g_strdup (buf);
1964 break;
1965
1966 case 2: /* prop value */
1967 value = xmmsv_ref (n->data);
1968
1969 /* Only insert the first source */
1970 if (g_hash_table_lookup (infos, name) == NULL) {
1971 g_hash_table_replace (infos, name, value);
1972 }
1973 break;
1974 }
1975
1976 xmmsv_unref (n->data);
1977 }
1978
1979 g_list_free (res);
1980
1981 return infos;
1982}
1983
1984/** Get the string associated to the property of the mediainfo
1985 * identified by the "field" attribute of the collection.
1986 *
1987 * @return The property value as a string.
1988 */
1989static gboolean
1990filter_get_mediainfo_field_string (xmmsv_coll_t *coll,
1991 GHashTable *mediainfo, gchar **val)
1992{
1993 gboolean retval = FALSE;
1994 gchar *attr;
1995 xmmsv_t *cmdval;
1996
1997 if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
1998 cmdval = g_hash_table_lookup (mediainfo, attr);
1999 if (cmdval != NULL) {
2000 switch (xmmsv_get_type (cmdval)) {
2001 case XMMSV_TYPE_STRING:
2002 {
2003 const gchar *s;
2004 xmmsv_get_string (cmdval, &s);
2005 *val = g_strdup (s);
2006 retval = TRUE;
2007 break;
2008 }
2009 case XMMSV_TYPE_INT32:
2010 {
2011 gint i;
2012 xmmsv_get_int (cmdval, &i);
2013 *val = g_strdup_printf ("%d", i);
2014 retval = TRUE;
2015 break;
2016 }
2017 default:
2018 break;
2019 }
2020 }
2021 }
2022
2023 return retval;
2024}
2025
2026/** Get the integer associated to the property of the mediainfo
2027 * identified by the "field" attribute of the collection.
2028 *
2029 * @return The property value as an integer.
2030 */
2031static gboolean
2032filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val)
2033{
2034 gboolean retval = FALSE;
2035 gchar *attr;
2036 xmmsv_t *cmdval;
2037
2038 if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
2039 cmdval = g_hash_table_lookup (mediainfo, attr);
2040 if (cmdval != NULL && xmmsv_get_type (cmdval) == XMMSV_TYPE_INT32) {
2041 xmmsv_get_int (cmdval, val);
2042 retval = TRUE;
2043 }
2044 }
2045
2046 return retval;
2047}
2048
2049/* Get the string value of the "value" attribute of the collection. */
2050static gboolean
2051filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val)
2052{
2053 gchar *attr;
2054 gboolean valid;
2055
2056 valid = xmmsv_coll_attribute_get (coll, "value", &attr);
2057 if (valid) {
2058 *val = attr;
2059 }
2060
2061 return valid;
2062}
2063
2064/* Get the integer value of the "value" attribute of the collection. */
2065static gboolean
2066filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val)
2067{
2068 gint buf;
2069 gboolean valid;
2070
2071 valid = xmms_collection_get_int_attr (coll, "value", &buf);
2072 if (valid) {
2073 *val = buf;
2074 }
2075
2076 return valid;
2077}
2078
2079/* Check whether the given operator has the "case-sensitive" attribute
2080 * or not. */
2081static gboolean
2082filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val)
2083{
2084 gchar *attr;
2085
2086 if (xmmsv_coll_attribute_get (coll, "case-sensitive", &attr)) {
2087 *val = (strcmp (attr, "true") == 0);
2088 }
2089 else {
2090 *val = FALSE;
2091 }
2092
2093 return TRUE;
2094}
2095
2096/* Check whether the HAS filter operator matches the mediainfo. */
2097static gboolean
2098xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2099 xmmsv_coll_t *coll, guint nsid,
2100 GHashTable *match_table)
2101{
2102 gboolean match = FALSE;
2103 gchar *mediaval;
2104
2105 /* If operator matches, recurse upwards in the operand */
2106 if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval)) {
2107 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2108 nsid, match_table);
2109
2110 g_free (mediaval);
2111 }
2112
2113 return match;
2114}
2115
2116/* Check whether the MATCH filter operator matches the mediainfo. */
2117static gboolean
2118xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2119 xmmsv_coll_t *coll, guint nsid,
2120 GHashTable *match_table)
2121{
2122 gboolean match = FALSE;
2123 gchar *mediaval = NULL;
2124 const gchar *opval;
2125 gboolean case_sens;
2126
2127 if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval) &&
2128 filter_get_operator_value_string (coll, &opval) &&
2129 filter_get_operator_case (coll, &case_sens)) {
2130
2131 if (case_sens) {
2132 match = (strcmp (mediaval, opval) == 0);
2133 } else {
2134 match = (g_ascii_strcasecmp (mediaval, opval) == 0);
2135 }
2136 }
2137
2138 /* If operator matches, recurse upwards in the operand */
2139 if (match) {
2140 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2141 nsid, match_table);
2142 }
2143
2144 if (mediaval != NULL) {
2145 g_free (mediaval);
2146 }
2147
2148 return match;
2149}
2150
2151/* Check whether the MATCH filter operator matches the mediainfo. */
2152static gboolean
2153xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2154 xmmsv_coll_t *coll, guint nsid,
2155 GHashTable *match_table)
2156{
2157 gboolean match = FALSE;
2158 gchar *buf, *opval, *mediaval;
2159 const gchar *s;
2160 gboolean case_sens;
2161
2162 if (filter_get_mediainfo_field_string (coll, mediainfo, &buf) &&
2163 filter_get_operator_value_string (coll, &s) &&
2164 filter_get_operator_case (coll, &case_sens)) {
2165
2166 /* Prepare values */
2167 if (case_sens) {
2168 opval = g_strdup (s);
2169 mediaval = g_strdup (buf);
2170 } else {
2171 opval = g_utf8_strdown (s, -1);
2172 mediaval = g_utf8_strdown (buf, -1);
2173 }
2174
2175 match = g_pattern_match_simple (opval, mediaval);
2176
2177 g_free (buf);
2178 g_free (opval);
2179 g_free (mediaval);
2180
2181 /* If operator matches, recurse upwards in the operand */
2182 if (match) {
2183 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2184 nsid, match_table);
2185 }
2186 }
2187
2188 return match;
2189}
2190
2191/* Check whether the SMALLER filter operator matches the mediainfo. */
2192static gboolean
2193xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2194 xmmsv_coll_t *coll, guint nsid,
2195 GHashTable *match_table)
2196{
2197 gboolean match = FALSE;
2198 gint mediaval;
2199 gint opval;
2200
2201 /* If operator matches, recurse upwards in the operand */
2202 if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
2203 filter_get_operator_value_int (coll, &opval) &&
2204 (mediaval < opval) ) {
2205
2206 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2207 nsid, match_table);
2208 }
2209
2210 return match;
2211}
2212
2213/* Check whether the GREATER filter operator matches the mediainfo. */
2214static gboolean
2215xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2216 xmmsv_coll_t *coll, guint nsid,
2217 GHashTable *match_table)
2218{
2219 gboolean match = FALSE;
2220 gint mediaval;
2221 gint opval;
2222
2223 /* If operator matches, recurse upwards in the operand */
2224 if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
2225 filter_get_operator_value_int (coll, &opval) &&
2226 (mediaval > opval) ) {
2227
2228 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2229 nsid, match_table);
2230 }
2231
2232 return match;
2233}
void xmms_collection_changed_msg_send(xmms_coll_dag_t *colldag, GTree *dict)
Definition: collection.c:163
const gchar * xmms_collection_get_namespace_string(xmms_collection_namespace_id_t nsid)
Find the namespace name (string) corresponding to a namespace id.
Definition: collection.c:1320
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
void xmms_collection_apply_to_collection(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, FuncApplyToColl f, void *udata)
Apply a function of type FuncApplyToColl to the given collection.
Definition: collection.c:1412
coll_find_state_t
Definition: collection.c:73
@ XMMS_COLLECTION_FIND_STATE_NOMATCH
Definition: collection.c:76
@ XMMS_COLLECTION_FIND_STATE_UNCHECKED
Definition: collection.c:74
@ XMMS_COLLECTION_FIND_STATE_MATCH
Definition: collection.c:75
xmms_collection_namespace_id_t xmms_collection_get_namespace_id(const gchar *namespace)
Find the namespace id corresponding to a namespace string.
Definition: collection.c:1297
void bind_all_references(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
If a reference, add the operator of the pointed collection as an operand.
Definition: collection.c:1503
#define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace)
Definition: collection.c:176
GTree * xmms_collection_changed_msg_new(xmmsc_collection_changed_actions_t type, const gchar *plname, const gchar *namespace)
Definition: collection.c:147
void xmms_collection_apply_to_all_collections(xmms_coll_dag_t *dag, FuncApplyToColl f, void *udata)
Apply a function of type FuncApplyToColl to all the collections in all namespaces.
Definition: collection.c:1393
struct add_metadata_from_tree_user_data_St add_metadata_from_tree_user_data_t
void xmms_collection_dag_save(xmms_coll_dag_t *dag)
Save the collection DAG in the database.
Definition: collserial.c:53
void xmms_collection_dag_restore(xmms_coll_dag_t *dag)
Restore the collection DAG from the database.
Definition: collserial.c:85
void xmms_coll_sync_schedule_sync()
Schedule a collection-to-database-synchronization in 10 seconds.
Definition: collsync.c:113
void xmms_coll_sync_shutdown()
Shutdown the collection-to-database-synchronization thread.
Definition: collsync.c:96
void xmms_coll_sync_init(xmms_coll_dag_t *dag)
Get the collection-to-database-synchronization thread running.
Definition: collsync.c:84
GString * xmms_collection_get_query(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, guint limit_start, guint limit_len, xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group)
Definition: collquery.c:97
void xmmsv_coll_remove_operand(xmmsv_coll_t *coll, xmmsv_coll_t *op)
Remove all the occurences of the operand in the given collection.
Definition: coll.c:226
struct xmmsv_St * xmmsv_coll_idlist_get(xmmsv_coll_t *coll)
Return the list of ids stored in the collection.
Definition: coll.c:429
xmmsv_coll_type_t xmmsv_coll_get_type(xmmsv_coll_t *coll)
Return the type of the collection.
Definition: coll.c:367
xmmsv_coll_t * xmmsv_coll_ref(xmmsv_coll_t *coll)
Increases the references for the xmmsv_coll_t.
Definition: coll.c:61
struct xmmsv_St * xmmsv_coll_operands_get(xmmsv_coll_t *coll)
Definition: coll.c:437
void xmmsv_coll_unref(xmmsv_coll_t *coll)
Decreases the references for the xmmsv_coll_t When the number of references reaches 0 it will be free...
Definition: coll.c:140
void xmmsv_coll_attribute_set(xmmsv_coll_t *coll, const char *key, const char *value)
Set an attribute in the given collection.
Definition: coll.c:460
int xmmsv_coll_idlist_append(xmmsv_coll_t *coll, int id)
Append a value to the idlist.
Definition: coll.c:252
xmmsv_coll_t * xmmsv_coll_new(xmmsv_coll_type_t type)
Allocate a new collection of the given type.
Definition: coll.c:78
void xmmsv_coll_add_operand(xmmsv_coll_t *coll, xmmsv_coll_t *op)
Add the operand to the given collection.
Definition: coll.c:195
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
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
void xmms_collection_dag_replace(xmms_coll_dag_t *dag, xmms_collection_namespace_id_t nsid, gchar *key, xmmsv_coll_t *newcoll)
Update the DAG to update the value of the pair with the given key.
Definition: collection.c:858
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
void xmms_collection_sync(xmms_coll_dag_t *dag)
Synchronize collection data to the database (i.e.
Definition: collection.c:539
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
int xmmsv_dict_foreach(xmmsv_t *dictv, xmmsv_dict_foreach_func func, void *user_data)
Apply a function to each key-element pair in the list.
Definition: value.c:1853
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_dict_remove(xmmsv_t *dictv, const char *key)
Remove the element corresponding to a given key in the dict xmmsv_t (if it exists).
Definition: value.c:1803
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_remove(xmmsv_list_iter_t *it)
Remove the element in the list at the position pointed at by the iterator.
Definition: value.c:1652
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
int xmmsv_list_iter_insert(xmmsv_list_iter_t *it, xmmsv_t *val)
Insert an element in the list at the position pointed at by the iterator.
Definition: value.c:1636
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
xmmsv_t * xmmsv_new_list(void)
Allocates a new list xmmsv_t.
Definition: value.c:250
int xmmsv_list_get_size(xmmsv_t *listv)
Return the size of the list.
Definition: value.c:1403
int xmmsv_list_clear(xmmsv_t *listv)
Empty the list from all its elements.
Definition: value.c:1356
int xmmsv_list_append(xmmsv_t *listv, xmmsv_t *val)
Append an element to the end of the list xmmsv_t.
Definition: value.c:1340
GList * xmms_medialib_select(xmms_medialib_session_t *session, const gchar *query, xmms_error_t *error)
Get a list of GHashTables 's that matches the query.
Definition: medialib.c:1380
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
guint32 xmms_medialib_source_to_id(xmms_medialib_session_t *session, const gchar *source)
Definition: medialib.c:261
GList * xmms_medialib_info_list(xmms_medialib_t *medialib, guint32 id, xmms_error_t *err)
Definition: medialib.c:1182
gboolean xmms_medialib_entry_property_set_str_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, const gchar *value, guint32 source)
Definition: medialib.c:632
gboolean xmms_medialib_entry_property_set_int_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, gint value, guint32 source)
Definition: medialib.c:582
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
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_ref(xmmsv_t *val)
References the xmmsv_t.
Definition: value.c:288
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
Definition: xmmsv_general.h:48
xmmsv_t * xmmsv_new_coll(xmmsv_coll_t *coll)
Allocates a new collection xmmsv_t.
Definition: value.c:202
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
Definition: xmmsv_general.h:43
@ XMMSV_TYPE_INT32
Definition: xmmsv_general.h:38
@ XMMSV_TYPE_STRING
Definition: xmmsv_general.h:39
struct xmms_xform_St xmms_xform_t
xmms_stream_type_t * _xmms_stream_type_new(const gchar *begin,...)
Definition: streamtype.c:362
xmms_xform_t * xmms_xform_chain_setup_url(xmms_medialib_entry_t entry, const gchar *url, GList *goal_formats, gboolean rehash)
Definition: xform.c:1405
GList * xmms_xform_browse_method(xmms_xform_t *xform, const gchar *url, xmms_error_t *error)
Definition: xform.c:257
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
#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
Definition: xmms_medialib.h:87
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
#define XMMS_STREAM_TYPE_BEGIN
@ XMMS_STREAM_TYPE_MIMETYPE
@ XMMS_STREAM_TYPE_END
struct xmms_stream_type_St xmms_stream_type_t
#define XMMS_COLLECTION_NUM_NAMESPACES
void(* FuncApplyToColl)(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
xmms_collection_namespace_id_t
@ XMMS_COLLECTION_NSID_ALL
@ XMMS_COLLECTION_NSID_INVALID
@ XMMS_COLLECTION_NSID_COLLECTIONS
@ XMMS_COLLECTION_NSID_PLAYLISTS
struct xmms_coll_dag_St xmms_coll_dag_t
#define xmms_error_iserror(e)
Definition: xmms_error.h:57
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
#define XMMS_MAX_INT_ATTRIBUTE_LEN
Definition: xmms_playlist.h:36
#define XMMS_PLAYLIST_COLLECTION_CHANGED_MSG(playlist, name)
Definition: xmms_playlist.h:68
@ XMMS_ERROR_GENERIC
@ XMMS_ERROR_NOENT
@ XMMS_ERROR_INVAL
@ XMMS_ERROR_NO_SAUSAGE
#define XMMS_COLLECTION_NS_ALL
#define XMMS_ACTIVE_PLAYLIST
#define XMMS_COLLECTION_NS_COLLECTIONS
xmmsv_coll_type_t
@ XMMS_COLLECTION_TYPE_UNION
@ XMMS_COLLECTION_TYPE_IDLIST
@ XMMS_COLLECTION_TYPE_INTERSECTION
@ XMMS_COLLECTION_TYPE_GREATER
@ XMMS_COLLECTION_TYPE_COMPLEMENT
@ XMMS_COLLECTION_TYPE_MATCH
@ XMMS_COLLECTION_TYPE_REFERENCE
@ XMMS_COLLECTION_TYPE_SMALLER
@ XMMS_COLLECTION_TYPE_EQUALS
@ XMMS_COLLECTION_TYPE_HAS
@ XMMS_COLLECTION_TYPE_QUEUE
@ XMMS_COLLECTION_TYPE_PARTYSHUFFLE
xmmsc_collection_changed_actions_t
@ XMMS_COLLECTION_CHANGED_RENAME
@ XMMS_COLLECTION_CHANGED_REMOVE
@ XMMS_COLLECTION_CHANGED_UPDATE
@ XMMS_COLLECTION_CHANGED_ADD
#define XMMS_COLLECTION_NS_PLAYLISTS
@ XMMS_IPC_SIGNAL_PLAYLIST_CHANGED
@ XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS
@ XMMS_IPC_SIGNAL_COLLECTION_CHANGED
@ XMMS_IPC_SIGNAL_PLAYLIST_LOADED
struct xmmsv_coll_St xmmsv_coll_t
Definition: xmmsv_coll.h:28