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