00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <stdio.h>
00023 #include <unistd.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <glib.h>
00027 #include <math.h>
00028
00029 #include "xmmspriv/xmms_collection.h"
00030 #include "xmmspriv/xmms_playlist.h"
00031 #include "xmmspriv/xmms_collquery.h"
00032 #include "xmmspriv/xmms_collserial.h"
00033 #include "xmmspriv/xmms_collsync.h"
00034 #include "xmmspriv/xmms_xform.h"
00035 #include "xmmspriv/xmms_streamtype.h"
00036 #include "xmms/xmms_ipc.h"
00037 #include "xmms/xmms_config.h"
00038 #include "xmms/xmms_log.h"
00039
00040
00041
00042
00043 typedef struct {
00044 const gchar *name;
00045 const gchar *namespace;
00046 xmmsv_coll_t *oldtarget;
00047 xmmsv_coll_t *newtarget;
00048 } coll_rebind_infos_t;
00049
00050 typedef struct {
00051 const gchar* oldname;
00052 const gchar* newname;
00053 const gchar* namespace;
00054 } coll_rename_infos_t;
00055
00056 typedef struct {
00057 xmms_coll_dag_t *dag;
00058 FuncApplyToColl func;
00059 void *udata;
00060 } coll_call_infos_t;
00061
00062 typedef struct {
00063 const gchar *target_name;
00064 const gchar *target_namespace;
00065 gboolean found;
00066 } coll_refcheck_t;
00067
00068 typedef struct {
00069 const gchar *key;
00070 xmmsv_coll_t *value;
00071 } coll_table_pair_t;
00072
00073 typedef enum {
00074 XMMS_COLLECTION_FIND_STATE_UNCHECKED,
00075 XMMS_COLLECTION_FIND_STATE_MATCH,
00076 XMMS_COLLECTION_FIND_STATE_NOMATCH,
00077 } coll_find_state_t;
00078
00079 typedef struct add_metadata_from_tree_user_data_St {
00080 xmms_medialib_session_t *session;
00081 xmms_medialib_entry_t entry;
00082 guint src;
00083 } add_metadata_from_tree_user_data_t;
00084
00085 static GList *global_stream_type;
00086
00087
00088
00089 static void xmms_collection_destroy (xmms_object_t *object);
00090
00091 static gboolean xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
00092 static gboolean xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
00093 static gboolean xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid);
00094
00095 static gboolean xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *tg_name, const gchar *tg_ns);
00096
00097 static void xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, FuncApplyToColl f, void *udata);
00098
00099 static void call_apply_to_coll (gpointer name, gpointer coll, gpointer udata);
00100 static void prepend_key_string (gpointer key, gpointer value, gpointer udata);
00101 static gboolean value_match_save_key (gpointer key, gpointer val, gpointer udata);
00102
00103 static void rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00104 static void rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00105 static void strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00106 static void check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00107
00108 static void coll_unref (void *coll);
00109
00110 static GHashTable *xmms_collection_media_info (xmms_medialib_entry_t mid, xmms_error_t *err);
00111
00112 static gboolean filter_get_mediainfo_field_string (xmmsv_coll_t *coll, GHashTable *mediainfo, gchar **val);
00113 static gboolean filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val);
00114 static gboolean filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val);
00115 static gboolean filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val);
00116 static gboolean filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val);
00117
00118 static void build_match_table (gpointer key, gpointer value, gpointer udata);
00119 static gboolean find_unchecked (gpointer name, gpointer value, gpointer udata);
00120 static void build_list_matches (gpointer key, gpointer value, gpointer udata);
00121
00122 static gboolean xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00123 static gboolean xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00124 static 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);
00125 static gboolean xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00126 static gboolean xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00127 static gboolean xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00128 static gboolean xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00129 static gboolean xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00130
00131 static xmmsv_coll_t * xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
00132 static GList * xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace, xmms_error_t *error);
00133 static void xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace, xmmsv_coll_t *coll, xmms_error_t *error);
00134 static void xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
00135 static GList * xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace, xmms_error_t *error);
00136 static 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);
00137
00138 static 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);
00139 static 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);
00140 static xmmsv_coll_t *xmms_collection_client_idlist_from_playlist (xmms_coll_dag_t *dag, const gchar *mediainfo, xmms_error_t *err);
00141 static void xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err);
00142
00143
00144 #include "collection_ipc.c"
00145
00146 GTree *
00147 xmms_collection_changed_msg_new (xmms_collection_changed_actions_t type,
00148 const gchar *plname, const gchar *namespace)
00149 {
00150 GTree *dict;
00151
00152 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
00153 NULL, (GDestroyNotify)xmmsv_unref);
00154
00155 g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type));
00156 g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (plname));
00157 g_tree_insert (dict, (gpointer) "namespace", xmmsv_new_string (namespace));
00158
00159 return dict;
00160 }
00161
00162 void
00163 xmms_collection_changed_msg_send (xmms_coll_dag_t *colldag, GTree *dict)
00164 {
00165 g_return_if_fail (colldag);
00166 g_return_if_fail (dict);
00167
00168 xmms_object_emit_f (XMMS_OBJECT (colldag),
00169 XMMS_IPC_SIGNAL_COLLECTION_CHANGED,
00170 XMMSV_TYPE_DICT,
00171 dict);
00172
00173 g_tree_destroy (dict);
00174 }
00175
00176 #define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace) xmms_collection_changed_msg_send (dag, xmms_collection_changed_msg_new (type, name, namespace))
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191 struct xmms_coll_dag_St {
00192 xmms_object_t object;
00193
00194
00195 xmms_playlist_t *playlist;
00196
00197 GHashTable *collrefs[XMMS_COLLECTION_NUM_NAMESPACES];
00198
00199 GMutex *mutex;
00200
00201 };
00202
00203 static void
00204 coll_sync_cb (xmms_object_t *object, xmmsv_t *val, gpointer udata)
00205 {
00206 xmms_coll_sync_schedule_sync ();
00207 }
00208
00209
00210
00211
00212
00213 xmms_coll_dag_t *
00214 xmms_collection_init (xmms_playlist_t *playlist)
00215 {
00216 gint i;
00217 xmms_coll_dag_t *ret;
00218 xmms_stream_type_t *f;
00219
00220 ret = xmms_object_new (xmms_coll_dag_t, xmms_collection_destroy);
00221 ret->mutex = g_mutex_new ();
00222 ret->playlist = playlist;
00223
00224 xmms_coll_sync_init (ret);
00225
00226 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
00227 ret->collrefs[i] = g_hash_table_new_full (g_str_hash, g_str_equal,
00228 g_free, coll_unref);
00229 }
00230
00231 xmms_collection_register_ipc_commands (XMMS_OBJECT (ret));
00232
00233
00234 xmms_object_connect (XMMS_OBJECT (ret),
00235 XMMS_IPC_SIGNAL_COLLECTION_CHANGED,
00236 coll_sync_cb, ret);
00237
00238
00239 xmms_object_connect (XMMS_OBJECT (playlist),
00240 XMMS_IPC_SIGNAL_PLAYLIST_CHANGED,
00241 coll_sync_cb, ret);
00242
00243 xmms_object_connect (XMMS_OBJECT (playlist),
00244 XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS,
00245 coll_sync_cb, ret);
00246
00247 xmms_object_connect (XMMS_OBJECT (playlist),
00248 XMMS_IPC_SIGNAL_PLAYLIST_LOADED,
00249 coll_sync_cb, ret);
00250
00251
00252 xmms_collection_dag_restore (ret);
00253
00254 f = _xmms_stream_type_new (XMMS_STREAM_TYPE_BEGIN,
00255 XMMS_STREAM_TYPE_MIMETYPE,
00256 "application/x-xmms2-playlist-entries",
00257 XMMS_STREAM_TYPE_END);
00258 global_stream_type = g_list_prepend (NULL, f);
00259
00260 return ret;
00261 }
00262
00263 static void
00264 add_metadata_from_tree (const gchar *key, xmmsv_t *value, gpointer user_data)
00265 {
00266 add_metadata_from_tree_user_data_t *ud = user_data;
00267
00268 if (xmmsv_get_type (value) == XMMSV_TYPE_INT32) {
00269 gint iv;
00270 xmmsv_get_int (value, &iv);
00271 xmms_medialib_entry_property_set_int_source (ud->session, ud->entry,
00272 key,
00273 iv,
00274 ud->src);
00275 } else if (xmmsv_get_type (value) == XMMSV_TYPE_STRING) {
00276 const gchar *sv;
00277 xmmsv_get_string (value, &sv);
00278 xmms_medialib_entry_property_set_str_source (ud->session, ud->entry,
00279 key,
00280 sv,
00281 ud->src);
00282 }
00283 }
00284
00285
00286
00287
00288
00289
00290
00291
00292 static xmmsv_coll_t *
00293 xmms_collection_client_idlist_from_playlist (xmms_coll_dag_t *dag,
00294 const gchar *path,
00295 xmms_error_t *err)
00296 {
00297 xmms_xform_t *xform;
00298 GList *lst, *n;
00299 xmmsv_coll_t *coll;
00300 xmms_medialib_session_t *session;
00301 guint src;
00302 const gchar *buf;
00303
00304
00305 xform = xmms_xform_chain_setup_url (0, path, global_stream_type, TRUE);
00306
00307 if (!xform) {
00308 xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "We can't handle this type of playlist or URL");
00309 return NULL;
00310 }
00311
00312 lst = xmms_xform_browse_method (xform, "/", err);
00313 if (xmms_error_iserror (err)) {
00314 xmms_object_unref (xform);
00315 return NULL;
00316 }
00317
00318 coll = xmmsv_coll_new (XMMS_COLLECTION_TYPE_IDLIST);
00319 session = xmms_medialib_begin_write ();
00320 src = xmms_medialib_source_to_id (session, "plugin/playlist");
00321
00322 n = lst;
00323 while (n) {
00324 xmms_medialib_entry_t entry;
00325
00326 xmmsv_t *a = n->data;
00327 xmmsv_t *b;
00328
00329 if (!xmmsv_dict_get (a, "realpath", &b)) {
00330 xmms_log_error ("Playlist plugin did not set realpath; probably a bug in plugin");
00331 xmmsv_unref (a);
00332 n = g_list_delete_link (n, n);
00333 continue;
00334 }
00335
00336 xmmsv_get_string (b, &buf);
00337 entry = xmms_medialib_entry_new_encoded (session, buf, err);
00338 xmmsv_dict_remove (a, "realpath");
00339 xmmsv_dict_remove (a, "path");
00340
00341 if (entry) {
00342 add_metadata_from_tree_user_data_t udata;
00343 udata.session = session;
00344 udata.entry = entry;
00345 udata.src = src;
00346
00347 xmmsv_dict_foreach(a, add_metadata_from_tree, &udata);
00348
00349 xmmsv_coll_idlist_append (coll, entry);
00350 } else {
00351 xmmsv_get_string (b, &buf);
00352 xmms_log_error ("couldn't add %s to collection!", buf);
00353 }
00354
00355 xmmsv_unref (a);
00356 n = g_list_delete_link (n, n);
00357 }
00358
00359 xmms_medialib_end (session);
00360 xmms_object_unref (xform);
00361
00362 return coll;
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375 void
00376 xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *name,
00377 const gchar *namespace, xmms_error_t *err)
00378 {
00379 guint nsid;
00380 gboolean retval = FALSE;
00381 guint i;
00382
00383 nsid = xmms_collection_get_namespace_id (namespace);
00384 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00385 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00386 return;
00387 }
00388
00389 g_mutex_lock (dag->mutex);
00390
00391
00392 if (nsid == XMMS_COLLECTION_NSID_ALL) {
00393 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
00394 retval = xmms_collection_unreference (dag, name, i) || retval;
00395 }
00396 } else {
00397 retval = xmms_collection_unreference (dag, name, nsid);
00398 }
00399
00400 g_mutex_unlock (dag->mutex);
00401
00402 if (retval == FALSE) {
00403 xmms_error_set (err, XMMS_ERROR_NOENT, "Failed to remove this collection!");
00404 }
00405
00406 }
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417 void
00418 xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace,
00419 xmmsv_coll_t *coll, xmms_error_t *err)
00420 {
00421 xmmsv_coll_t *existing;
00422 guint nsid;
00423 const gchar *alias;
00424 gchar *newkey = NULL;
00425
00426 nsid = xmms_collection_get_namespace_id (namespace);
00427 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00428 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00429 return;
00430 } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
00431 xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot save collection in all namespaces");
00432 return;
00433 }
00434
00435
00436 if (!xmms_collection_validate (dag, coll, name, namespace)) {
00437 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
00438 return;
00439 }
00440
00441 g_mutex_lock (dag->mutex);
00442
00443
00444 existing = xmms_collection_get_pointer (dag, name, nsid);
00445 if (existing != NULL) {
00446
00447 coll_rebind_infos_t infos = { name, namespace, existing, coll };
00448 xmms_collection_apply_to_all_collections (dag, rebind_references, &infos);
00449 }
00450
00451
00452 xmms_collection_apply_to_collection (dag, coll, bind_all_references, NULL);
00453
00454
00455 if (existing != NULL) {
00456 while ((alias = xmms_collection_find_alias (dag, nsid,
00457 existing, NULL)) != NULL) {
00458 newkey = g_strdup (alias);
00459
00460
00461 xmms_collection_dag_replace (dag, nsid, newkey, coll);
00462 xmmsv_coll_ref (coll);
00463
00464 XMMS_COLLECTION_CHANGED_MSG (XMMS_COLLECTION_CHANGED_UPDATE,
00465 newkey,
00466 namespace);
00467 }
00468
00469
00470 } else {
00471 newkey = g_strdup (name);
00472 xmms_collection_dag_replace (dag, nsid, newkey, coll);
00473 xmmsv_coll_ref (coll);
00474
00475 XMMS_COLLECTION_CHANGED_MSG (XMMS_COLLECTION_CHANGED_ADD,
00476 newkey,
00477 namespace);
00478 }
00479
00480 g_mutex_unlock (dag->mutex);
00481
00482
00483 if (nsid == XMMS_COLLECTION_NSID_PLAYLISTS) {
00484 XMMS_PLAYLIST_COLLECTION_CHANGED_MSG (dag->playlist, newkey);
00485 }
00486
00487 }
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500 xmmsv_coll_t *
00501 xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *name,
00502 const gchar *namespace, xmms_error_t *err)
00503 {
00504 xmmsv_coll_t *coll = NULL;
00505 guint nsid;
00506
00507 nsid = xmms_collection_get_namespace_id (namespace);
00508 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00509 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00510 return NULL;
00511 }
00512
00513 g_mutex_lock (dag->mutex);
00514
00515 coll = xmms_collection_get_pointer (dag, name, nsid);
00516
00517
00518 if (coll == NULL) {
00519 xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
00520
00521
00522 } else {
00523 xmmsv_coll_ref (coll);
00524 }
00525
00526 g_mutex_unlock (dag->mutex);
00527
00528 return coll;
00529 }
00530
00531
00532
00533
00534
00535
00536
00537
00538 void
00539 xmms_collection_sync (xmms_coll_dag_t *dag)
00540 {
00541 g_return_if_fail (dag);
00542
00543 g_mutex_lock (dag->mutex);
00544
00545 xmms_collection_dag_save (dag);
00546
00547 g_mutex_unlock (dag->mutex);
00548 }
00549
00550
00551 void
00552 xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err)
00553 {
00554 xmms_collection_sync (dag);
00555 }
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567 GList *
00568 xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace,
00569 xmms_error_t *err)
00570 {
00571 GList *r = NULL;
00572 guint nsid;
00573
00574 nsid = xmms_collection_get_namespace_id (namespace);
00575 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00576 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00577 return NULL;
00578 }
00579
00580 g_mutex_lock (dag->mutex);
00581
00582
00583 xmms_collection_foreach_in_namespace (dag, nsid, prepend_key_string, &r);
00584
00585 g_mutex_unlock (dag->mutex);
00586
00587 return r;
00588 }
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599 GList *
00600 xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace,
00601 xmms_error_t *err)
00602 {
00603 GHashTable *mediainfo;
00604 GList *ret = NULL;
00605 guint nsid;
00606 gchar *open_name;
00607 GHashTable *match_table;
00608 xmmsv_coll_t *coll;
00609
00610
00611 nsid = xmms_collection_get_namespace_id (namespace);
00612 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00613 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00614 return NULL;
00615 }
00616 if (nsid == XMMS_COLLECTION_NSID_ALL) {
00617 xmms_error_set (err, XMMS_ERROR_INVAL, "cannot search in all namespaces");
00618 return NULL;
00619 }
00620
00621
00622 match_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
00623 xmms_collection_foreach_in_namespace (dag, nsid, build_match_table, match_table);
00624
00625
00626 mediainfo = xmms_collection_media_info (mid, err);
00627
00628
00629 while (g_hash_table_find (match_table, find_unchecked, &open_name) != NULL) {
00630 coll_find_state_t *match = g_new (coll_find_state_t, 1);
00631 coll = xmms_collection_get_pointer (dag, open_name, nsid);
00632 if (xmms_collection_media_match (dag, mediainfo, coll, nsid, match_table)) {
00633 *match = XMMS_COLLECTION_FIND_STATE_MATCH;
00634 } else {
00635 *match = XMMS_COLLECTION_FIND_STATE_NOMATCH;
00636 }
00637 g_hash_table_replace (match_table, g_strdup (open_name), match);
00638 }
00639
00640
00641 g_hash_table_foreach (match_table, build_list_matches, &ret);
00642 g_hash_table_destroy (match_table);
00643
00644 g_hash_table_destroy (mediainfo);
00645
00646 return ret;
00647 }
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659 void
00660 xmms_collection_client_rename (xmms_coll_dag_t *dag, const gchar *from_name,
00661 const gchar *to_name, const gchar *namespace,
00662 xmms_error_t *err)
00663 {
00664 gboolean retval;
00665 guint nsid;
00666 xmmsv_coll_t *from_coll, *to_coll;
00667
00668 nsid = xmms_collection_get_namespace_id (namespace);
00669 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00670 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00671 return;
00672 } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
00673 xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot rename collection in all namespaces");
00674 return;
00675 }
00676
00677 g_mutex_lock (dag->mutex);
00678
00679 from_coll = xmms_collection_get_pointer (dag, from_name, nsid);
00680 to_coll = xmms_collection_get_pointer (dag, to_name, nsid);
00681
00682
00683 if (from_coll == NULL) {
00684 xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
00685 retval = FALSE;
00686
00687 } else if (to_coll != NULL) {
00688 xmms_error_set (err, XMMS_ERROR_NOENT, "a collection already exists with the target name");
00689 retval = FALSE;
00690
00691
00692 } else {
00693 GTree *dict;
00694
00695
00696 xmms_collection_dag_replace (dag, nsid, g_strdup (to_name), from_coll);
00697 xmmsv_coll_ref (from_coll);
00698
00699
00700 g_hash_table_remove (dag->collrefs[nsid], from_name);
00701
00702
00703 coll_rename_infos_t infos = { from_name, to_name, namespace };
00704 xmms_collection_apply_to_all_collections (dag, rename_references, &infos);
00705
00706
00707 dict = xmms_collection_changed_msg_new (XMMS_COLLECTION_CHANGED_RENAME,
00708 from_name, namespace);
00709 g_tree_insert (dict, (gpointer) "newname", xmmsv_new_string (to_name));
00710 xmms_collection_changed_msg_send (dag, dict);
00711
00712 retval = TRUE;
00713 }
00714
00715 g_mutex_unlock (dag->mutex);
00716
00717 }
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730 GList *
00731 xmms_collection_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
00732 gint32 lim_start, gint32 lim_len, xmmsv_t *order,
00733 xmms_error_t *err)
00734 {
00735 GList *res, *n;
00736 xmmsv_t *fetch, *group, *idval;
00737
00738
00739 group = xmmsv_new_list ();
00740 fetch = xmmsv_new_list ();
00741 idval = xmmsv_new_string ("id");
00742 xmmsv_list_append (fetch, idval);
00743
00744 res = xmms_collection_client_query_infos (dag, coll, lim_start, lim_len, order, fetch, group, err);
00745
00746
00747 for (n = res; n; n = n->next) {
00748 xmms_medialib_entry_t id;
00749 xmmsv_t *id_val, *cmdval = n->data;
00750
00751 xmmsv_dict_get (cmdval, "id", &id_val);
00752 xmmsv_get_int (id_val, &id);
00753 n->data = xmmsv_new_int (id);
00754
00755 xmmsv_unref (cmdval);
00756 }
00757
00758 xmmsv_unref (group);
00759 xmmsv_unref (fetch);
00760 xmmsv_unref (idval);
00761
00762 return res;
00763 }
00764
00765
00766 GList *
00767 xmms_collection_client_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
00768 gint32 lim_start, gint32 lim_len, xmmsv_t *order,
00769 xmms_error_t *err)
00770 {
00771 return xmms_collection_query_ids (dag, coll, lim_start, lim_len, order, err);
00772 }
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785 GList *
00786 xmms_collection_client_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
00787 gint32 lim_start, gint32 lim_len, xmmsv_t *order,
00788 xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err)
00789 {
00790 GList *res = NULL;
00791 GString *query;
00792
00793
00794 if (xmmsv_list_get_size (fetch) == 0) {
00795 xmms_error_set (err, XMMS_ERROR_INVAL, "fetch list must not be empty!");
00796 return NULL;
00797 }
00798
00799
00800 if (!check_string_list (order)) {
00801 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid order list!");
00802 return NULL;
00803 }
00804 if (!check_string_list (fetch)) {
00805 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid fetch list!");
00806 return NULL;
00807 }
00808 if (!check_string_list (group)) {
00809 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid group list!");
00810 return NULL;
00811 }
00812
00813
00814 if (!xmms_collection_validate (dag, coll, NULL, NULL)) {
00815 if (err) {
00816 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
00817 }
00818 return NULL;
00819 }
00820
00821 g_mutex_lock (dag->mutex);
00822
00823 query = xmms_collection_get_query (dag, coll, lim_start, lim_len,
00824 order, fetch, group);
00825
00826 g_mutex_unlock (dag->mutex);
00827
00828 XMMS_DBG ("COLLECTIONS: query_infos with %s", query->str);
00829
00830
00831 xmms_medialib_session_t *session = xmms_medialib_begin ();
00832 res = xmms_medialib_select (session, query->str, err);
00833 xmms_medialib_end (session);
00834
00835 g_string_free (query, TRUE);
00836
00837 return res;
00838 }
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848 void
00849 xmms_collection_update_pointer (xmms_coll_dag_t *dag, const gchar *name,
00850 guint nsid, xmmsv_coll_t *newtarget)
00851 {
00852 xmms_collection_dag_replace (dag, nsid, g_strdup (name), newtarget);
00853 xmmsv_coll_ref (newtarget);
00854 }
00855
00856
00857 void
00858 xmms_collection_dag_replace (xmms_coll_dag_t *dag,
00859 xmms_collection_namespace_id_t nsid,
00860 gchar *key, xmmsv_coll_t *newcoll)
00861 {
00862 g_hash_table_replace (dag->collrefs[nsid], key, newcoll);
00863 }
00864
00865
00866
00867
00868
00869
00870
00871
00872 xmmsv_coll_t *
00873 xmms_collection_get_pointer (xmms_coll_dag_t *dag, const gchar *collname,
00874 guint nsid)
00875 {
00876 gint i;
00877 xmmsv_coll_t *coll = NULL;
00878
00879 if (nsid == XMMS_COLLECTION_NSID_ALL) {
00880 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES && coll == NULL; ++i) {
00881 coll = g_hash_table_lookup (dag->collrefs[i], collname);
00882 }
00883 } else {
00884 coll = g_hash_table_lookup (dag->collrefs[nsid], collname);
00885 }
00886
00887 return coll;
00888 }
00889
00890
00891
00892
00893
00894
00895
00896
00897 gboolean
00898 xmms_collection_get_int_attr (xmmsv_coll_t *coll, const gchar *attrname, gint *val)
00899 {
00900 gboolean retval = FALSE;
00901 gint buf;
00902 gchar *str;
00903 gchar *endptr;
00904
00905 if (xmmsv_coll_attribute_get (coll, attrname, &str)) {
00906 buf = strtol (str, &endptr, 10);
00907
00908
00909 if (*endptr == '\0') {
00910 *val = buf;
00911 retval = TRUE;
00912 }
00913 }
00914
00915 return retval;
00916 }
00917
00918
00919
00920
00921
00922
00923
00924
00925 gboolean
00926 xmms_collection_set_int_attr (xmmsv_coll_t *coll, const gchar *attrname,
00927 gint newval)
00928 {
00929 gboolean retval = FALSE;
00930 gchar str[XMMS_MAX_INT_ATTRIBUTE_LEN + 1];
00931 gint written;
00932
00933 written = g_snprintf (str, sizeof (str), "%d", newval);
00934 if (written < XMMS_MAX_INT_ATTRIBUTE_LEN) {
00935 xmmsv_coll_attribute_set (coll, attrname, str);
00936 retval = TRUE;
00937 }
00938
00939 return retval;
00940 }
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954 const gchar *
00955 xmms_collection_find_alias (xmms_coll_dag_t *dag, guint nsid,
00956 xmmsv_coll_t *value, const gchar *key)
00957 {
00958 const gchar *otherkey = NULL;
00959 coll_table_pair_t search_pair = { key, value };
00960
00961 if (g_hash_table_find (dag->collrefs[nsid], value_match_save_key,
00962 &search_pair) != NULL) {
00963 otherkey = search_pair.key;
00964 }
00965
00966 return otherkey;
00967 }
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977 xmms_medialib_entry_t
00978 xmms_collection_get_random_media (xmms_coll_dag_t *dag, xmmsv_coll_t *source)
00979 {
00980 GList *res;
00981 xmms_medialib_entry_t mid = 0;
00982 xmmsv_t *rorder = xmmsv_new_list ();
00983 xmmsv_t *randval = xmmsv_new_string ("~RANDOM()");
00984
00985
00986 xmmsv_list_append (rorder, randval);
00987
00988 res = xmms_collection_query_ids (dag, source, 0, 1, rorder, NULL);
00989
00990 if (res != NULL) {
00991 xmmsv_t *val = (xmmsv_t *) res->data;
00992 xmmsv_get_int (val, &mid);
00993 xmmsv_unref (val);
00994 g_list_free (res);
00995 }
00996
00997 xmmsv_unref (rorder);
00998 xmmsv_unref (randval);
00999
01000 return mid;
01001 }
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011 static void
01012 xmms_collection_destroy (xmms_object_t *object)
01013 {
01014 gint i;
01015 xmms_coll_dag_t *dag = (xmms_coll_dag_t *)object;
01016
01017 g_return_if_fail (dag);
01018
01019 xmms_coll_sync_shutdown ();
01020 xmms_collection_dag_save (dag);
01021
01022 g_mutex_free (dag->mutex);
01023
01024 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
01025 g_hash_table_destroy (dag->collrefs[i]);
01026 }
01027
01028 xmms_collection_unregister_ipc_commands ();
01029 }
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039
01040
01041 static gboolean
01042 xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
01043 const gchar *save_name, const gchar *save_namespace)
01044 {
01045
01046 if (save_namespace != NULL &&
01047 strcmp (save_namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
01048
01049 if (xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_IDLIST &&
01050 xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_QUEUE &&
01051 xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_PARTYSHUFFLE) {
01052 return FALSE;
01053 }
01054 }
01055
01056
01057 return xmms_collection_validate_recurs (dag, coll, save_name,
01058 save_namespace);
01059 }
01060
01061
01062
01063
01064
01065 static gboolean
01066 xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
01067 const gchar *save_name, const gchar *save_namespace)
01068 {
01069 guint num_operands = 0;
01070 xmmsv_coll_t *op, *ref;
01071 gchar *attr, *attr2;
01072 gboolean valid = TRUE;
01073 xmmsv_coll_type_t type;
01074 xmms_collection_namespace_id_t nsid;
01075
01076
01077 num_operands = xmmsv_list_get_size (xmmsv_coll_operands_get (coll));
01078
01079
01080 type = xmmsv_coll_get_type (coll);
01081 switch (type) {
01082 case XMMS_COLLECTION_TYPE_REFERENCE:
01083
01084 if (num_operands > 1) {
01085 return FALSE;
01086 }
01087
01088
01089 xmmsv_coll_attribute_get (coll, "reference", &attr);
01090 if (attr == NULL) {
01091 return FALSE;
01092 } else if (strcmp (attr, "All Media") != 0) {
01093 xmmsv_coll_attribute_get (coll, "namespace", &attr2);
01094
01095 if (attr2 == NULL) {
01096 return FALSE;
01097 }
01098
01099 nsid = xmms_collection_get_namespace_id (attr2);
01100 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
01101 return FALSE;
01102 }
01103
01104 g_mutex_lock (dag->mutex);
01105 ref = xmms_collection_get_pointer (dag, attr, nsid);
01106 if (ref == NULL) {
01107 g_mutex_unlock (dag->mutex);
01108 return FALSE;
01109 }
01110
01111 if (save_name && save_namespace) {
01112
01113 if (strcmp (attr, save_name) == 0 &&
01114 strcmp (attr2, save_namespace) == 0) {
01115
01116 g_mutex_unlock (dag->mutex);
01117 return FALSE;
01118
01119
01120 } else if (xmms_collection_has_reference_to (dag, ref, save_name,
01121 save_namespace)) {
01122 g_mutex_unlock (dag->mutex);
01123 return FALSE;
01124 }
01125 }
01126
01127 g_mutex_unlock (dag->mutex);
01128 } else {
01129
01130 ref = NULL;
01131 }
01132
01133
01134 if (num_operands == 1) {
01135 xmmsv_t *val;
01136 xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &val);
01137 xmmsv_get_coll (val, &op);
01138
01139 if (op != ref) {
01140 return FALSE;
01141 }
01142 }
01143 break;
01144
01145 case XMMS_COLLECTION_TYPE_UNION:
01146 case XMMS_COLLECTION_TYPE_INTERSECTION:
01147
01148 if (num_operands == 0) {
01149 return FALSE;
01150 }
01151 break;
01152
01153 case XMMS_COLLECTION_TYPE_COMPLEMENT:
01154
01155 if (num_operands != 1) {
01156 return FALSE;
01157 }
01158 break;
01159
01160 case XMMS_COLLECTION_TYPE_HAS:
01161
01162 if (num_operands != 1) {
01163 return FALSE;
01164 }
01165
01166
01167
01168 if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
01169 return FALSE;
01170 }
01171 break;
01172
01173 case XMMS_COLLECTION_TYPE_EQUALS:
01174 case XMMS_COLLECTION_TYPE_MATCH:
01175 case XMMS_COLLECTION_TYPE_SMALLER:
01176 case XMMS_COLLECTION_TYPE_GREATER:
01177
01178 if (num_operands != 1) {
01179 return FALSE;
01180 }
01181
01182
01183
01184 if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
01185 return FALSE;
01186 }
01187
01188
01189
01190
01191
01192
01193 if (!xmmsv_coll_attribute_get (coll, "value", &attr)) {
01194 return FALSE;
01195 }
01196 break;
01197
01198 case XMMS_COLLECTION_TYPE_IDLIST:
01199 case XMMS_COLLECTION_TYPE_QUEUE:
01200
01201 if (num_operands > 0) {
01202 return FALSE;
01203 }
01204 break;
01205
01206 case XMMS_COLLECTION_TYPE_PARTYSHUFFLE:
01207
01208 if (num_operands != 1) {
01209 return FALSE;
01210 }
01211 break;
01212
01213
01214 default:
01215 return FALSE;
01216 break;
01217 }
01218
01219
01220
01221 if (num_operands > 0 && type != XMMS_COLLECTION_TYPE_REFERENCE) {
01222 xmmsv_list_iter_t *iter;
01223 xmmsv_get_list_iter (xmmsv_coll_operands_get (coll), &iter);
01224
01225 for (xmmsv_list_iter_first (iter);
01226 valid && xmmsv_list_iter_valid (iter);
01227 xmmsv_list_iter_next (iter)) {
01228
01229 xmmsv_t *val;
01230 xmmsv_list_iter_entry (iter, &val);
01231 xmmsv_get_coll (val, &op);
01232
01233 if (!xmms_collection_validate_recurs (dag, op, save_name,
01234 save_namespace)) {
01235 valid = FALSE;
01236 }
01237 }
01238
01239 xmmsv_list_iter_explicit_destroy (iter);
01240 }
01241
01242 return valid;
01243 }
01244
01245
01246
01247
01248
01249
01250
01251
01252 static gboolean
01253 xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid)
01254 {
01255 xmmsv_coll_t *existing, *active_pl;
01256 gboolean retval = FALSE;
01257
01258 existing = g_hash_table_lookup (dag->collrefs[nsid], name);
01259 active_pl = g_hash_table_lookup (dag->collrefs[XMMS_COLLECTION_NSID_PLAYLISTS],
01260 XMMS_ACTIVE_PLAYLIST);
01261
01262
01263 if (existing != NULL && existing != active_pl) {
01264 const gchar *matchkey;
01265 const gchar *nsname = xmms_collection_get_namespace_string (nsid);
01266 coll_rebind_infos_t infos = { name, nsname, existing, NULL };
01267
01268
01269
01270
01271
01272 xmms_collection_apply_to_all_collections (dag, strip_references, &infos);
01273
01274
01275 while ((matchkey = xmms_collection_find_alias (dag, nsid,
01276 existing, NULL)) != NULL) {
01277
01278 XMMS_COLLECTION_CHANGED_MSG (XMMS_COLLECTION_CHANGED_REMOVE,
01279 matchkey,
01280 nsname);
01281
01282 g_hash_table_remove (dag->collrefs[nsid], matchkey);
01283 }
01284
01285 retval = TRUE;
01286 }
01287
01288 return retval;
01289 }
01290
01291
01292
01293
01294
01295
01296 xmms_collection_namespace_id_t
01297 xmms_collection_get_namespace_id (const gchar *namespace)
01298 {
01299 guint nsid;
01300
01301 if (strcmp (namespace, XMMS_COLLECTION_NS_ALL) == 0) {
01302 nsid = XMMS_COLLECTION_NSID_ALL;
01303 } else if (strcmp (namespace, XMMS_COLLECTION_NS_COLLECTIONS) == 0) {
01304 nsid = XMMS_COLLECTION_NSID_COLLECTIONS;
01305 } else if (strcmp (namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
01306 nsid = XMMS_COLLECTION_NSID_PLAYLISTS;
01307 } else {
01308 nsid = XMMS_COLLECTION_NSID_INVALID;
01309 }
01310
01311 return nsid;
01312 }
01313
01314
01315
01316
01317
01318
01319 const gchar *
01320 xmms_collection_get_namespace_string (xmms_collection_namespace_id_t nsid)
01321 {
01322 const gchar *name;
01323
01324 switch (nsid) {
01325 case XMMS_COLLECTION_NSID_ALL:
01326 name = XMMS_COLLECTION_NS_ALL;
01327 break;
01328 case XMMS_COLLECTION_NSID_COLLECTIONS:
01329 name = XMMS_COLLECTION_NS_COLLECTIONS;
01330 break;
01331 case XMMS_COLLECTION_NSID_PLAYLISTS:
01332 name = XMMS_COLLECTION_NS_PLAYLISTS;
01333 break;
01334
01335 case XMMS_COLLECTION_NSID_INVALID:
01336 default:
01337 name = NULL;
01338 break;
01339 }
01340
01341 return name;
01342 }
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354 static gboolean
01355 xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
01356 const gchar *tg_name, const gchar *tg_ns)
01357 {
01358 coll_refcheck_t check = { tg_name, tg_ns, FALSE };
01359 xmms_collection_apply_to_collection (dag, coll, check_for_reference, &check);
01360
01361 return check.found;
01362 }
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372 void
01373 xmms_collection_foreach_in_namespace (xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
01374 {
01375 gint i;
01376
01377 if (nsid == XMMS_COLLECTION_NSID_ALL) {
01378 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
01379 g_hash_table_foreach (dag->collrefs[i], f, udata);
01380 }
01381 } else if (nsid != XMMS_COLLECTION_NSID_INVALID) {
01382 g_hash_table_foreach (dag->collrefs[nsid], f, udata);
01383 }
01384 }
01385
01386
01387
01388
01389
01390
01391
01392 void
01393 xmms_collection_apply_to_all_collections (xmms_coll_dag_t *dag,
01394 FuncApplyToColl f, void *udata)
01395 {
01396 gint i;
01397 coll_call_infos_t callinfos = { dag, f, udata };
01398
01399 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
01400 g_hash_table_foreach (dag->collrefs[i], call_apply_to_coll, &callinfos);
01401 }
01402 }
01403
01404
01405
01406
01407
01408
01409
01410
01411 void
01412 xmms_collection_apply_to_collection (xmms_coll_dag_t *dag,
01413 xmmsv_coll_t *coll,
01414 FuncApplyToColl f, void *udata)
01415 {
01416 xmms_collection_apply_to_collection_recurs (dag, coll, NULL, f, udata);
01417 }
01418
01419
01420 static void
01421 xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag,
01422 xmmsv_coll_t *coll,
01423 xmmsv_coll_t *parent,
01424 FuncApplyToColl f, void *udata)
01425 {
01426 xmmsv_coll_t *op;
01427
01428
01429 f (dag, coll, parent, udata);
01430
01431
01432 if (xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_REFERENCE) {
01433 xmmsv_list_iter_t *iter;
01434 xmmsv_get_list_iter (xmmsv_coll_operands_get (coll), &iter);
01435
01436 for (xmmsv_list_iter_first (iter);
01437 xmmsv_list_iter_valid (iter);
01438 xmmsv_list_iter_next (iter)) {
01439
01440 xmmsv_t *val;
01441 xmmsv_list_iter_entry (iter, &val);
01442
01443 xmmsv_get_coll (val, &op);
01444
01445 xmms_collection_apply_to_collection_recurs (dag, op, coll, f,
01446 udata);
01447 }
01448
01449 xmmsv_list_iter_explicit_destroy (iter);
01450 }
01451 }
01452
01453
01454
01455
01456
01457 static void
01458 call_apply_to_coll (gpointer name, gpointer coll, gpointer udata)
01459 {
01460 coll_call_infos_t *callinfos = (coll_call_infos_t*)udata;
01461
01462 xmms_collection_apply_to_collection (callinfos->dag, coll,
01463 callinfos->func, callinfos->udata);
01464 }
01465
01466
01467
01468
01469 static void
01470 prepend_key_string (gpointer key, gpointer value, gpointer udata)
01471 {
01472 GList **list = (GList**)udata;
01473 *list = g_list_prepend (*list, xmmsv_new_string (key));
01474 }
01475
01476
01477
01478
01479
01480
01481 static gboolean
01482 value_match_save_key (gpointer key, gpointer val, gpointer udata)
01483 {
01484 gboolean found = FALSE;
01485 coll_table_pair_t *pair = (coll_table_pair_t*)udata;
01486 xmmsv_coll_t *coll = (xmmsv_coll_t*)val;
01487
01488
01489 if ((coll == pair->value) &&
01490 (pair->key == NULL || strcmp (pair->key, key) != 0)) {
01491 pair->key = key;
01492 found = TRUE;
01493 }
01494
01495 return found;
01496 }
01497
01498
01499
01500
01501
01502 void
01503 bind_all_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01504 {
01505 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE) {
01506 xmmsv_coll_t *target;
01507 gchar *target_name;
01508 gchar *target_namespace;
01509 gint target_nsid;
01510
01511 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01512 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01513 if (target_name == NULL || target_namespace == NULL ||
01514 strcmp (target_name, "All Media") == 0) {
01515 return;
01516 }
01517
01518 target_nsid = xmms_collection_get_namespace_id (target_namespace);
01519 if (target_nsid == XMMS_COLLECTION_NSID_INVALID) {
01520 return;
01521 }
01522
01523 target = xmms_collection_get_pointer (dag, target_name, target_nsid);
01524 if (target == NULL) {
01525 return;
01526 }
01527
01528 xmmsv_coll_add_operand (coll, target);
01529 }
01530 }
01531
01532
01533
01534
01535
01536
01537 static void
01538 rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01539 {
01540 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE) {
01541 coll_rebind_infos_t *infos;
01542
01543 gchar *target_name = NULL;
01544 gchar *target_namespace = NULL;
01545
01546 infos = (coll_rebind_infos_t*)udata;
01547
01548
01549
01550 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01551 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01552 if (strcmp (infos->name, target_name) != 0 ||
01553 strcmp (infos->namespace, target_namespace) != 0) {
01554 return;
01555 }
01556
01557 xmmsv_coll_remove_operand (coll, infos->oldtarget);
01558 xmmsv_coll_add_operand (coll, infos->newtarget);
01559 }
01560 }
01561
01562
01563
01564
01565
01566 static void
01567 rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01568 {
01569 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE) {
01570 coll_rename_infos_t *infos;
01571
01572 gchar *target_name = NULL;
01573 gchar *target_namespace = NULL;
01574
01575 infos = (coll_rename_infos_t*)udata;
01576
01577 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01578 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01579 if (strcmp (infos->oldname, target_name) == 0 &&
01580 strcmp (infos->namespace, target_namespace) == 0) {
01581 xmmsv_coll_attribute_set (coll, "reference", infos->newname);
01582 }
01583 }
01584 }
01585
01586
01587
01588
01589
01590 static void
01591 strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01592 {
01593 xmmsv_coll_t *op;
01594 coll_rebind_infos_t *infos;
01595 gchar *target_name = NULL;
01596 gchar *target_namespace = NULL;
01597 xmmsv_list_iter_t *iter;
01598 xmmsv_t *tmp;
01599
01600 infos = (coll_rebind_infos_t*)udata;
01601
01602 xmmsv_get_list_iter (xmmsv_coll_operands_get (coll), &iter);
01603 for (xmmsv_list_iter_first (iter);
01604 xmmsv_list_iter_valid (iter);
01605 xmmsv_list_iter_next (iter)) {
01606
01607 xmmsv_list_iter_entry (iter, &tmp);
01608 xmmsv_get_coll (tmp, &op);
01609
01610
01611 if (xmmsv_coll_get_type (op) != XMMS_COLLECTION_TYPE_REFERENCE) {
01612 continue;
01613 }
01614
01615 xmmsv_coll_attribute_get (op, "reference", &target_name);
01616 xmmsv_coll_attribute_get (op, "namespace", &target_namespace);
01617 if (strcmp (infos->name, target_name) != 0 ||
01618 strcmp (infos->namespace, target_namespace) != 0) {
01619 continue;
01620 }
01621
01622
01623
01624 xmmsv_list_clear (xmmsv_coll_operands_get (op));
01625
01626 xmmsv_list_iter_remove (iter);
01627
01628 tmp = xmmsv_new_coll (infos->oldtarget);
01629 xmmsv_list_iter_insert (iter, tmp);
01630 xmmsv_unref (tmp);
01631 }
01632 xmmsv_list_iter_explicit_destroy (iter);
01633 }
01634
01635
01636
01637
01638
01639 static void
01640 check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01641 {
01642 coll_refcheck_t *check = (coll_refcheck_t*)udata;
01643 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE && !check->found) {
01644 gchar *target_name, *target_namespace;
01645
01646 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01647 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01648 if (strcmp (check->target_name, target_name) == 0 &&
01649 strcmp (check->target_namespace, target_namespace) == 0) {
01650 check->found = TRUE;
01651 } else {
01652 xmmsv_coll_t *op;
01653 xmmsv_t *tmp;
01654
01655 if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
01656 xmmsv_get_coll (tmp, &op);
01657 xmms_collection_apply_to_collection_recurs (dag, op, coll,
01658 check_for_reference,
01659 udata);
01660 }
01661 }
01662 }
01663 }
01664
01665
01666
01667
01668
01669
01670 static void
01671 coll_unref (void *coll)
01672 {
01673 xmmsv_coll_unref (coll);
01674 }
01675
01676
01677
01678
01679
01680
01681 static void
01682 build_match_table (gpointer key, gpointer value, gpointer udata)
01683 {
01684 GHashTable *match_table = udata;
01685 coll_find_state_t *match = g_new (coll_find_state_t, 1);
01686 *match = XMMS_COLLECTION_FIND_STATE_UNCHECKED;
01687 g_hash_table_replace (match_table, g_strdup (key), match);
01688 }
01689
01690
01691
01692
01693 static gboolean
01694 find_unchecked (gpointer name, gpointer value, gpointer udata)
01695 {
01696 coll_find_state_t *match = value;
01697 gchar **open = udata;
01698 *open = name;
01699 return (*match == XMMS_COLLECTION_FIND_STATE_UNCHECKED);
01700 }
01701
01702
01703
01704
01705 static void
01706 build_list_matches (gpointer key, gpointer value, gpointer udata)
01707 {
01708 gchar *coll_name = key;
01709 coll_find_state_t *state = value;
01710 GList **list = udata;
01711 if (*state == XMMS_COLLECTION_FIND_STATE_MATCH) {
01712 *list = g_list_prepend (*list, xmmsv_new_string (coll_name));
01713 }
01714 }
01715
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725 static gboolean
01726 xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
01727 xmmsv_coll_t *coll, guint nsid,
01728 GHashTable *match_table)
01729 {
01730 gboolean match = FALSE;
01731 xmmsv_coll_t *op;
01732 gchar *attr1 = NULL, *attr2 = NULL;
01733 xmmsv_t *val;
01734 xmms_medialib_entry_t entry, id;
01735 xmmsv_list_iter_t *iter;
01736
01737 switch (xmmsv_coll_get_type (coll)) {
01738 case XMMS_COLLECTION_TYPE_REFERENCE:
01739 if (xmmsv_coll_attribute_get (coll, "reference", &attr1)) {
01740 if (strcmp (attr1, "All Media") == 0) {
01741 match = TRUE;
01742 } else if (xmmsv_coll_attribute_get (coll, "namespace", &attr2)) {
01743 match = xmms_collection_media_match_reference (dag, mediainfo,
01744 coll, nsid,
01745 match_table,
01746 attr1, attr2);
01747 }
01748 }
01749 break;
01750
01751 case XMMS_COLLECTION_TYPE_UNION:
01752
01753 xmmsv_get_list_iter (xmmsv_coll_operands_get (coll), &iter);
01754
01755 for (xmmsv_list_iter_first (iter);
01756 !match && xmmsv_list_iter_valid (iter);
01757 xmmsv_list_iter_next (iter)) {
01758
01759 xmmsv_list_iter_entry (iter, &val);
01760 xmmsv_get_coll (val, &op);
01761
01762 match = xmms_collection_media_match (dag, mediainfo, op,
01763 nsid, match_table);
01764 }
01765 xmmsv_list_iter_explicit_destroy (iter);
01766 break;
01767
01768 case XMMS_COLLECTION_TYPE_INTERSECTION:
01769
01770 match = TRUE;
01771 xmmsv_get_list_iter (xmmsv_coll_operands_get (coll), &iter);
01772
01773 for (xmmsv_list_iter_first (iter);
01774 match && xmmsv_list_iter_valid (iter);
01775 xmmsv_list_iter_next (iter)) {
01776
01777 xmmsv_list_iter_entry (iter, &val);
01778 xmmsv_get_coll (val, &op);
01779
01780 match = xmms_collection_media_match (dag, mediainfo, op,
01781 nsid, match_table);
01782 }
01783 xmmsv_list_iter_explicit_destroy (iter);
01784 break;
01785
01786 case XMMS_COLLECTION_TYPE_COMPLEMENT:
01787
01788 match = !xmms_collection_media_match_operand (dag, mediainfo, coll,
01789 nsid, match_table);
01790 break;
01791
01792 case XMMS_COLLECTION_TYPE_HAS:
01793 match = xmms_collection_media_filter_has (dag, mediainfo, coll,
01794 nsid, match_table);
01795 break;
01796
01797 case XMMS_COLLECTION_TYPE_EQUALS:
01798 match = xmms_collection_media_filter_equals (dag, mediainfo, coll,
01799 nsid, match_table);
01800 break;
01801
01802 case XMMS_COLLECTION_TYPE_MATCH:
01803 match = xmms_collection_media_filter_match (dag, mediainfo, coll,
01804 nsid, match_table);
01805 break;
01806
01807 case XMMS_COLLECTION_TYPE_SMALLER:
01808 match = xmms_collection_media_filter_smaller (dag, mediainfo, coll,
01809 nsid, match_table);
01810 break;
01811
01812 case XMMS_COLLECTION_TYPE_GREATER:
01813 match = xmms_collection_media_filter_greater (dag, mediainfo, coll,
01814 nsid, match_table);
01815 break;
01816
01817 case XMMS_COLLECTION_TYPE_IDLIST:
01818 case XMMS_COLLECTION_TYPE_QUEUE:
01819 case XMMS_COLLECTION_TYPE_PARTYSHUFFLE:
01820
01821 val = g_hash_table_lookup (mediainfo, "id");
01822 if (val != NULL) {
01823 xmmsv_get_int (val, &id);
01824
01825 xmmsv_get_list_iter (xmmsv_coll_idlist_get (coll), &iter);
01826 for (xmmsv_list_iter_first (iter);
01827 xmmsv_list_iter_valid (iter);
01828 xmmsv_list_iter_next (iter)) {
01829
01830 xmmsv_list_iter_entry_int (iter, &entry);
01831 if (entry == id) {
01832 match = TRUE;
01833 break;
01834 }
01835 }
01836 xmmsv_list_iter_explicit_destroy (iter);
01837 }
01838 break;
01839
01840
01841 default:
01842 XMMS_DBG ("invalid collection operator in xmms_collection_media_match");
01843 g_assert_not_reached ();
01844 break;
01845 }
01846
01847 return match;
01848 }
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861 static gboolean
01862 xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo,
01863 xmmsv_coll_t *coll, guint nsid,
01864 GHashTable *match_table,
01865 const gchar *refname, const gchar *refns)
01866 {
01867 gboolean match;
01868 guint refnsid;
01869 coll_find_state_t *matchstate;
01870
01871
01872 refnsid = xmms_collection_get_namespace_id (refns);
01873 if (refnsid == nsid) {
01874 matchstate = g_hash_table_lookup (match_table, refname);
01875 if (*matchstate == XMMS_COLLECTION_FIND_STATE_UNCHECKED) {
01876
01877 matchstate = g_new (coll_find_state_t, 1);
01878 match = xmms_collection_media_match_operand (dag,
01879 mediainfo,
01880 coll, nsid,
01881 match_table);
01882
01883 if (match) {
01884 *matchstate = XMMS_COLLECTION_FIND_STATE_MATCH;
01885 } else {
01886 *matchstate = XMMS_COLLECTION_FIND_STATE_NOMATCH;
01887 }
01888
01889 g_hash_table_replace (match_table, g_strdup (refname), matchstate);
01890
01891 } else {
01892 match = (*matchstate == XMMS_COLLECTION_FIND_STATE_MATCH);
01893 }
01894
01895
01896 } else {
01897 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
01898 nsid, match_table);
01899 }
01900
01901 return match;
01902 }
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914 static gboolean
01915 xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo,
01916 xmmsv_coll_t *coll, guint nsid,
01917 GHashTable *match_table)
01918 {
01919 xmmsv_coll_t *op;
01920 xmmsv_t *tmp;
01921 gboolean match = FALSE;
01922
01923 if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
01924 xmmsv_get_coll (tmp, &op);
01925
01926 match = xmms_collection_media_match (dag, mediainfo, op, nsid, match_table);
01927 }
01928
01929 return match;
01930 }
01931
01932
01933
01934
01935
01936
01937 static GHashTable *
01938 xmms_collection_media_info (xmms_medialib_entry_t mid, xmms_error_t *err)
01939 {
01940 GList *res;
01941 GList *n;
01942 GHashTable *infos;
01943 gchar *name;
01944 const gchar *buf;
01945 xmmsv_t *cmdval;
01946 xmmsv_t *value;
01947 guint state;
01948
01949
01950 res = xmms_medialib_info_list (NULL, mid, err);
01951
01952
01953 infos = g_hash_table_new_full (g_str_hash, g_str_equal,
01954 g_free, (GDestroyNotify) xmmsv_unref);
01955 for (state = 0, n = res; n; state = (state + 1) % 3, n = n->next) {
01956 switch (state) {
01957 case 0:
01958 break;
01959
01960 case 1:
01961 cmdval = n->data;
01962 xmmsv_get_string (cmdval, &buf);
01963 name = g_strdup (buf);
01964 break;
01965
01966 case 2:
01967 value = xmmsv_ref (n->data);
01968
01969
01970 if (g_hash_table_lookup (infos, name) == NULL) {
01971 g_hash_table_replace (infos, name, value);
01972 }
01973 break;
01974 }
01975
01976 xmmsv_unref (n->data);
01977 }
01978
01979 g_list_free (res);
01980
01981 return infos;
01982 }
01983
01984
01985
01986
01987
01988
01989 static gboolean
01990 filter_get_mediainfo_field_string (xmmsv_coll_t *coll,
01991 GHashTable *mediainfo, gchar **val)
01992 {
01993 gboolean retval = FALSE;
01994 gchar *attr;
01995 xmmsv_t *cmdval;
01996
01997 if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
01998 cmdval = g_hash_table_lookup (mediainfo, attr);
01999 if (cmdval != NULL) {
02000 switch (xmmsv_get_type (cmdval)) {
02001 case XMMSV_TYPE_STRING:
02002 {
02003 const gchar *s;
02004 xmmsv_get_string (cmdval, &s);
02005 *val = g_strdup (s);
02006 retval = TRUE;
02007 break;
02008 }
02009 case XMMSV_TYPE_INT32:
02010 {
02011 gint i;
02012 xmmsv_get_int (cmdval, &i);
02013 *val = g_strdup_printf ("%d", i);
02014 retval = TRUE;
02015 break;
02016 }
02017 default:
02018 break;
02019 }
02020 }
02021 }
02022
02023 return retval;
02024 }
02025
02026
02027
02028
02029
02030
02031 static gboolean
02032 filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val)
02033 {
02034 gboolean retval = FALSE;
02035 gchar *attr;
02036 xmmsv_t *cmdval;
02037
02038 if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
02039 cmdval = g_hash_table_lookup (mediainfo, attr);
02040 if (cmdval != NULL && xmmsv_get_type (cmdval) == XMMSV_TYPE_INT32) {
02041 xmmsv_get_int (cmdval, val);
02042 retval = TRUE;
02043 }
02044 }
02045
02046 return retval;
02047 }
02048
02049
02050 static gboolean
02051 filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val)
02052 {
02053 gchar *attr;
02054 gboolean valid;
02055
02056 valid = xmmsv_coll_attribute_get (coll, "value", &attr);
02057 if (valid) {
02058 *val = attr;
02059 }
02060
02061 return valid;
02062 }
02063
02064
02065 static gboolean
02066 filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val)
02067 {
02068 gint buf;
02069 gboolean valid;
02070
02071 valid = xmms_collection_get_int_attr (coll, "value", &buf);
02072 if (valid) {
02073 *val = buf;
02074 }
02075
02076 return valid;
02077 }
02078
02079
02080
02081 static gboolean
02082 filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val)
02083 {
02084 gchar *attr;
02085
02086 if (xmmsv_coll_attribute_get (coll, "case-sensitive", &attr)) {
02087 *val = (strcmp (attr, "true") == 0);
02088 }
02089 else {
02090 *val = FALSE;
02091 }
02092
02093 return TRUE;
02094 }
02095
02096
02097 static gboolean
02098 xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02099 xmmsv_coll_t *coll, guint nsid,
02100 GHashTable *match_table)
02101 {
02102 gboolean match = FALSE;
02103 gchar *mediaval;
02104
02105
02106 if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval)) {
02107 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02108 nsid, match_table);
02109
02110 g_free (mediaval);
02111 }
02112
02113 return match;
02114 }
02115
02116
02117 static gboolean
02118 xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02119 xmmsv_coll_t *coll, guint nsid,
02120 GHashTable *match_table)
02121 {
02122 gboolean match = FALSE;
02123 gchar *mediaval = NULL;
02124 const gchar *opval;
02125 gboolean case_sens;
02126
02127 if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval) &&
02128 filter_get_operator_value_string (coll, &opval) &&
02129 filter_get_operator_case (coll, &case_sens)) {
02130
02131 if (case_sens) {
02132 match = (strcmp (mediaval, opval) == 0);
02133 } else {
02134 match = (g_ascii_strcasecmp (mediaval, opval) == 0);
02135 }
02136 }
02137
02138
02139 if (match) {
02140 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02141 nsid, match_table);
02142 }
02143
02144 if (mediaval != NULL) {
02145 g_free (mediaval);
02146 }
02147
02148 return match;
02149 }
02150
02151
02152 static gboolean
02153 xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02154 xmmsv_coll_t *coll, guint nsid,
02155 GHashTable *match_table)
02156 {
02157 gboolean match = FALSE;
02158 gchar *buf, *opval, *mediaval;
02159 const gchar *s;
02160 gboolean case_sens;
02161
02162 if (filter_get_mediainfo_field_string (coll, mediainfo, &buf) &&
02163 filter_get_operator_value_string (coll, &s) &&
02164 filter_get_operator_case (coll, &case_sens)) {
02165
02166
02167 if (case_sens) {
02168 opval = g_strdup (s);
02169 mediaval = g_strdup (buf);
02170 } else {
02171 opval = g_utf8_strdown (s, -1);
02172 mediaval = g_utf8_strdown (buf, -1);
02173 }
02174
02175 match = g_pattern_match_simple (opval, mediaval);
02176
02177 g_free (buf);
02178 g_free (opval);
02179 g_free (mediaval);
02180
02181
02182 if (match) {
02183 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02184 nsid, match_table);
02185 }
02186 }
02187
02188 return match;
02189 }
02190
02191
02192 static gboolean
02193 xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02194 xmmsv_coll_t *coll, guint nsid,
02195 GHashTable *match_table)
02196 {
02197 gboolean match = FALSE;
02198 gint mediaval;
02199 gint opval;
02200
02201
02202 if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
02203 filter_get_operator_value_int (coll, &opval) &&
02204 (mediaval < opval) ) {
02205
02206 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02207 nsid, match_table);
02208 }
02209
02210 return match;
02211 }
02212
02213
02214 static gboolean
02215 xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02216 xmmsv_coll_t *coll, guint nsid,
02217 GHashTable *match_table)
02218 {
02219 gboolean match = FALSE;
02220 gint mediaval;
02221 gint opval;
02222
02223
02224 if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
02225 filter_get_operator_value_int (coll, &opval) &&
02226 (mediaval > opval) ) {
02227
02228 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02229 nsid, match_table);
02230 }
02231
02232 return match;
02233 }