[rhythmbox] dbus-media-server: add artist/album/genre trees
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] dbus-media-server: add artist/album/genre trees
- Date: Fri, 5 Aug 2011 12:36:40 +0000 (UTC)
commit 34927db670f6bd3ae71a1a8c6c5888aaf42dc743
Author: Jonathan Matthew <jonathan d14n org>
Date: Fri Aug 5 22:35:15 2011 +1000
dbus-media-server: add artist/album/genre trees
Only one level, and only for the library so far. Music item
display names are now a bit more interesting too.
.../rb-dbus-media-server-plugin.c | 1251 +++++++++++++++++---
1 files changed, 1110 insertions(+), 141 deletions(-)
---
diff --git a/plugins/dbus-media-server/rb-dbus-media-server-plugin.c b/plugins/dbus-media-server/rb-dbus-media-server-plugin.c
index b4c863f..58f91ac 100644
--- a/plugins/dbus-media-server/rb-dbus-media-server-plugin.c
+++ b/plugins/dbus-media-server/rb-dbus-media-server-plugin.c
@@ -45,6 +45,7 @@
#include <sources/rb-display-page-model.h>
#include <sources/rb-playlist-source.h>
#include <sources/rb-removable-media-source.h>
+#include <rhythmdb/rhythmdb-property-model.h>
#define RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN (rb_dbus_media_server_plugin_get_type ())
#define RB_DBUS_MEDIA_SERVER_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN, RBMediaServer2Plugin))
@@ -117,15 +118,33 @@ typedef struct
char *dbus_path;
char *parent_dbus_path;
+ gboolean flat;
+ guint all_tracks_reg_id[2];
+ GList *properties;
+
RBMediaServer2Plugin *plugin;
} SourceRegistrationData;
+typedef struct
+{
+ SourceRegistrationData *source_data;
+ char *dbus_path;
+ char *display_name;
+ guint dbus_object_id[2];
+ guint dbus_subtree_id;
+ RhythmDBPropType property;
+ RhythmDBPropertyModel *model;
+ gboolean updated;
+ GList *updated_values;
+} SourcePropertyRegistrationData;
+
RB_DEFINE_PLUGIN(RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN, RBMediaServer2Plugin, rb_dbus_media_server_plugin,)
G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module);
static void unregister_source_container (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data, gboolean deactivating);
-static void emit_source_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data);
+static void emit_source_tracks_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data);
+static void emit_property_value_property_updates (RBMediaServer2Plugin *plugin, SourcePropertyRegistrationData *source_data, RBRefString *value);
static void emit_category_container_property_updates (RBMediaServer2Plugin *plugin, CategoryRegistrationData *category_data);
static void emit_root_property_updates (RBMediaServer2Plugin *plugin);
@@ -240,14 +259,14 @@ entry_property_maps (RhythmDBPropType prop)
}
static GVariant *
-get_entry_property_value (RhythmDBEntry *entry, const char *property_name)
+get_entry_property_value (RhythmDBEntry *entry, const char *property_name, RhythmDBPropType context)
{
GVariant *v;
if (g_strcmp0 (property_name, "Parent") == 0) {
return g_variant_new_object_path (RB_MEDIASERVER2_ROOT);
} else if (g_strcmp0 (property_name, "Type") == 0) {
- return g_variant_new_string ("audio"); /* .music? */
+ return g_variant_new_string ("music");
} else if (g_strcmp0 (property_name, "Path") == 0) {
char *path;
@@ -257,7 +276,36 @@ get_entry_property_value (RhythmDBEntry *entry, const char *property_name)
g_free (path);
return v;
} else if (g_strcmp0 (property_name, "DisplayName") == 0) {
- return g_variant_new_string (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+ char *display;
+ /* need to consider disc numbers in here too */
+ switch (context) {
+ case RHYTHMDB_PROP_ARTIST:
+ /* use <album> <track> <title> */
+ display = g_strdup_printf ("%s: %lu. %s",
+ rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM),
+ rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
+ rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+ break;
+ case RHYTHMDB_PROP_ALBUM:
+ /* use <track> <artist> <title> */
+ display = g_strdup_printf ("%lu. %s - %s",
+ rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
+ rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
+ rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+ break;
+ case RHYTHMDB_PROP_GENRE:
+ default:
+ /* use <album> <track> <artist> <title> */
+ display = g_strdup_printf ("%s: %lu. %s - %s",
+ rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM),
+ rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
+ rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
+ rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+ break;
+ }
+ v = g_variant_new_string (display);
+ g_free (display);
+ return v;
} else if (g_strcmp0 (property_name, "URLs") == 0) {
const char *urls[] = { NULL, NULL };
char *url;
@@ -322,41 +370,712 @@ get_entry_property (GDBusConnection *connection,
return NULL;
}
- return get_entry_property_value (entry, property_name);
+ return get_entry_property_value (entry, property_name, RHYTHMDB_PROP_ENTRY_ID);
+}
+
+static const GDBusInterfaceVTable entry_vtable =
+{
+ NULL,
+ (GDBusInterfaceGetPropertyFunc) get_entry_property,
+ NULL
+};
+
+static char **
+enumerate_entry_subtree (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ RBMediaServer2Plugin *plugin)
+{
+ return (char **)g_new0(char *, 1);
+}
+
+static GDBusInterfaceInfo **
+introspect_entry_subtree (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *node,
+ RBMediaServer2Plugin *plugin)
+{
+ GPtrArray *p;
+ GDBusInterfaceInfo *i;
+
+ p = g_ptr_array_new ();
+
+ i = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
+ g_ptr_array_add (p, g_dbus_interface_info_ref (i));
+
+ i = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_ITEM_IFACE_NAME);
+ g_ptr_array_add (p, g_dbus_interface_info_ref (i));
+
+ g_ptr_array_add (p, NULL);
+
+ return (GDBusInterfaceInfo **)g_ptr_array_free (p, FALSE);
+}
+
+static const GDBusInterfaceVTable *
+dispatch_entry_subtree (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *node,
+ gpointer *out_user_data,
+ RBMediaServer2Plugin *plugin)
+{
+ *out_user_data = plugin;
+ return &entry_vtable;
+}
+
+static GDBusSubtreeVTable entry_subtree_vtable =
+{
+ (GDBusSubtreeEnumerateFunc) enumerate_entry_subtree,
+ (GDBusSubtreeIntrospectFunc) introspect_entry_subtree,
+ (GDBusSubtreeDispatchFunc) dispatch_entry_subtree
+};
+
+/* containers in general */
+
+static void
+emit_updated (GDBusConnection *connection, const char *path)
+{
+ GError *error = NULL;
+ g_dbus_connection_emit_signal (connection,
+ NULL,
+ path,
+ MEDIA_SERVER2_CONTAINER_IFACE_NAME,
+ "Updated",
+ NULL,
+ &error);
+ if (error != NULL) {
+ g_warning ("Unable to emit Updated signal for MediaServer2 container %s: %s",
+ path,
+ error->message);
+ g_clear_error (&error);
+ }
+}
+
+static gboolean
+emit_container_updated_cb (RBMediaServer2Plugin *plugin)
+{
+ GList *l, *ll, *lll;
+
+ rb_debug ("emitting updates");
+ /* source containers */
+ for (l = plugin->sources; l != NULL; l = l->next) {
+ SourceRegistrationData *source_data = l->data;
+
+ /* property containers */
+ for (ll = source_data->properties; ll != NULL; ll = ll->next) {
+ SourcePropertyRegistrationData *prop_data = ll->data;
+
+ /* emit value updates */
+ for (lll = prop_data->updated_values; lll != NULL; lll = lll->next) {
+ RBRefString *value = lll->data;
+ emit_property_value_property_updates (plugin, prop_data, value);
+ }
+ rb_list_destroy_free (prop_data->updated_values, (GDestroyNotify)rb_refstring_unref);
+
+ if (prop_data->updated) {
+ emit_updated (plugin->connection, prop_data->dbus_path);
+ prop_data->updated = FALSE;
+ }
+ }
+
+ if (source_data->updated) {
+ emit_source_tracks_property_updates (plugin, source_data);
+ if (source_data->flat) {
+ emit_updated (plugin->connection, source_data->dbus_path);
+ } else {
+ char *path;
+ path = g_strdup_printf ("%s/all", source_data->dbus_path);
+ emit_updated (plugin->connection, path);
+ g_free (path);
+ }
+ source_data->updated = FALSE;
+ }
+
+ }
+
+ /* source categories */
+ for (l = plugin->categories; l != NULL; l = l->next) {
+ CategoryRegistrationData *category_data = l->data;
+ if (category_data->updated) {
+ emit_category_container_property_updates (plugin, category_data);
+ emit_updated (plugin->connection, category_data->dbus_path);
+ category_data->updated = FALSE;
+ }
+ }
+
+ /* root */
+ if (plugin->root_updated) {
+ emit_root_property_updates (plugin);
+ emit_updated (plugin->connection, RB_MEDIASERVER2_ROOT);
+ plugin->root_updated = FALSE;
+ }
+
+ rb_debug ("done emitting updates");
+ plugin->emit_updated_id = 0;
+ return FALSE;
+}
+
+static void
+emit_updated_in_idle (RBMediaServer2Plugin *plugin)
+{
+ if (plugin->emit_updated_id == 0) {
+ plugin->emit_updated_id =
+ g_idle_add ((GSourceFunc)emit_container_updated_cb, plugin);
+ }
+}
+
+
+/* property value source subcontainers (source/year/1995) */
+
+static char *
+encode_property_value (const char *value)
+{
+ char *encoded;
+ const char *hex = "0123456789ABCDEF";
+ char *d;
+ char c;
+
+ encoded = g_malloc0 (strlen (value) * 3 + 1);
+ d = encoded;
+ while (*value != '\0') {
+ c = *value++;
+ if (g_ascii_isalnum (c)) {
+ *d++ = c;
+ } else {
+ guint8 v = (guint8)c;
+ *d++ = '_';
+ *d++ = hex[(v >> 4) & 0x0f];
+ *d++ = hex[v & 0x0f];
+ }
+ }
+
+ return encoded;
+}
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+static char *
+decode_property_value (char *encoded)
+{
+ char *decoded;
+ char *e;
+ char *d;
+
+ decoded = g_malloc0 (strlen (encoded) + 1);
+ d = decoded;
+ e = encoded;
+ while (*e != '\0') {
+ if (*e != '_') {
+ *d++ = *e++;
+ } else if (e[1] != '\0' && e[2] != '\0') {
+ *d++ = HEXCHAR(e);
+ e += 3;
+ } else {
+ /* broken */
+ break;
+ }
+ }
+
+ return decoded;
+}
+
+static char *
+extract_property_value (RhythmDB *db, const char *object_path)
+{
+ char **bits;
+ char *value;
+ int nbits;
+
+ bits = g_strsplit (object_path, "/", 0);
+ nbits = g_strv_length (bits);
+
+ value = decode_property_value (bits[nbits-1]);
+ g_strfreev (bits);
+ return value;
+}
+
+static void
+property_value_method_call (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ SourcePropertyRegistrationData *data)
+{
+ GVariantBuilder *list;
+ RhythmDB *db;
+ char *value;
+
+ if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) != 0) {
+ rb_debug ("method call on unexpected interface %s", interface_name);
+ return;
+ }
+
+ db = data->source_data->plugin->db;
+ value = extract_property_value (db, object_path);
+
+ if (g_strcmp0 (method_name, "ListChildren") == 0 ||
+ g_strcmp0 (method_name, "ListItems") == 0) {
+ RhythmDBQuery *base;
+ RhythmDBQuery *query;
+ RhythmDBQueryModel *query_model;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint list_offset;
+ guint list_max;
+ char **filter;
+ guint count = 0;
+
+ /* consider caching query models? */
+ g_object_get (data->source_data->base_query_model, "query", &base, NULL);
+ query = rhythmdb_query_copy (base);
+
+ rhythmdb_query_append (db,
+ query,
+ RHYTHMDB_QUERY_PROP_EQUALS, data->property, value,
+ RHYTHMDB_QUERY_END);
+ /* maybe use a result list? */
+ query_model = rhythmdb_query_model_new_empty (db);
+ rhythmdb_do_full_query_parsed (db, RHYTHMDB_QUERY_RESULTS (query_model), query);
+ rhythmdb_query_free (query);
+
+ g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
+ list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
+
+ if (rb_str_in_strv ("*", (const char **)filter)) {
+ g_strfreev (filter);
+ filter = g_strdupv ((char **)all_entry_properties);
+ }
+
+ model = GTK_TREE_MODEL (query_model);
+ if (gtk_tree_model_get_iter_first (model, &iter)) {
+ do {
+ RhythmDBEntry *entry;
+ GVariantBuilder *eb;
+ int i;
+ if (list_max > 0 && count == list_max) {
+ break;
+ }
+
+ entry = rhythmdb_query_model_iter_to_entry (query_model, &iter);
+ if (entry == NULL) {
+ continue;
+ }
+
+ if (list_offset > 0) {
+ list_offset--;
+ continue;
+ }
+
+ eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+ for (i = 0; filter[i] != NULL; i++) {
+ GVariant *v;
+ v = get_entry_property_value (entry, filter[i], data->property);
+ if (v != NULL) {
+ g_variant_builder_add (eb, "{sv}", filter[i], v);
+ }
+ }
+
+ g_variant_builder_add (list, "a{sv}", eb);
+ count++;
+
+ } while (gtk_tree_model_iter_next (model, &iter));
+ }
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
+ g_variant_builder_unref (list);
+
+ g_strfreev (filter);
+ } else if (g_strcmp0 (method_name, "ListContainers") == 0) {
+ list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
+ g_variant_builder_unref (list);
+ } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Method %s.%s not supported",
+ interface_name,
+ method_name);
+ }
+
+ g_free (value);
+}
+
+static guint
+get_property_value_count (SourcePropertyRegistrationData *data, const char *value)
+{
+ guint entry_count = 0;
+ GtkTreeIter iter;
+
+ if (rhythmdb_property_model_iter_from_string (data->model, value, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (data->model), &iter,
+ RHYTHMDB_PROPERTY_MODEL_COLUMN_NUMBER, &entry_count,
+ -1);
+ }
+ return entry_count;
+}
+
+static GVariant *
+get_property_value_property (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *property_name,
+ GError **error,
+ SourcePropertyRegistrationData *data)
+{
+ RhythmDB *db;
+ GVariant *v = NULL;
+ char *value;
+
+ db = data->source_data->plugin->db;
+ value = extract_property_value (db, object_path);
+
+ if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
+ if (g_strcmp0 (property_name, "Parent") == 0) {
+ v = g_variant_new_object_path (data->dbus_path);
+ } else if (g_strcmp0 (property_name, "Type") == 0) {
+ v = g_variant_new_string ("container");
+ } else if (g_strcmp0 (property_name, "Path") == 0) {
+ v = g_variant_new_string (object_path);
+ } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
+ v = g_variant_new_string (value);
+ }
+ } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
+
+ if (g_strcmp0 (property_name, "ChildCount") == 0 ||
+ g_strcmp0 (property_name, "ItemCount") == 0) {
+ v = g_variant_new_uint32 (get_property_value_count (data, value));
+ } else if (g_strcmp0 (property_name, "ContainerCount") == 0) {
+ v = g_variant_new_uint32 (0);
+ } else if (g_strcmp0 (property_name, "Searchable") == 0) {
+ v = g_variant_new_boolean (FALSE);
+ }
+ }
+
+ if (v == NULL) {
+ g_set_error (error,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Property %s.%s not supported",
+ interface_name,
+ property_name);
+ }
+ g_free (value);
+ return v;
+}
+
+static void
+emit_property_value_property_updates (RBMediaServer2Plugin *plugin, SourcePropertyRegistrationData *data, RBRefString *value)
+{
+ GError *error = NULL;
+ const char *invalidated[] = { NULL };
+ GVariantBuilder *properties;
+ GVariant *parameters;
+ GVariant *v;
+ char *encoded;
+ char *path;
+
+ rb_debug ("updating properties for %s/%s", data->dbus_path, rb_refstring_get (value));
+ properties = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+
+ v = g_variant_new_uint32 (get_property_value_count (data, rb_refstring_get (value)));
+ g_variant_builder_add (properties, "{sv}", "ItemCount", v);
+ g_variant_builder_add (properties, "{sv}", "ChildCount", v);
+ g_variant_builder_add (properties, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
+
+ encoded = encode_property_value (rb_refstring_get (value));
+ path = g_strdup_printf ("%s/%s", data->dbus_path, encoded);
+ g_free (encoded);
+
+ parameters = g_variant_new ("(sa{sv}^as)",
+ MEDIA_SERVER2_CONTAINER_IFACE_NAME,
+ properties,
+ invalidated);
+ g_variant_builder_unref (properties);
+ g_dbus_connection_emit_signal (plugin->connection,
+ NULL,
+ path,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ parameters,
+ &error);
+ if (error != NULL) {
+ g_warning ("Unable to send property changes for MediaServer2 container %s: %s",
+ path,
+ error->message);
+ g_clear_error (&error);
+ }
+
+ emit_updated (plugin->connection, path);
+
+ g_free (encoded);
+ g_free (path);
+}
+
+static const GDBusInterfaceVTable property_value_vtable =
+{
+ (GDBusInterfaceMethodCallFunc) property_value_method_call,
+ (GDBusInterfaceGetPropertyFunc) get_property_value_property,
+ NULL
+};
+
+/* property-based source subcontainers (source/year/) */
+
+static void
+property_container_method_call (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ SourcePropertyRegistrationData *data)
+{
+ GVariantBuilder *list;
+
+ if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) != 0) {
+ rb_debug ("method call on unexpected interface %s", interface_name);
+ return;
+ }
+
+ if (g_strcmp0 (method_name, "ListChildren") == 0 ||
+ g_strcmp0 (method_name, "ListContainers") == 0) {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint list_offset;
+ guint list_max;
+ const char **filter;
+ guint count = 0;
+ gboolean all_props;
+
+ g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
+ list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
+
+ all_props = rb_str_in_strv ("*", filter);
+
+ model = GTK_TREE_MODEL (data->model);
+ if (gtk_tree_model_get_iter_first (model, &iter)) {
+ /* skip 'all' row */
+ while (gtk_tree_model_iter_next (model, &iter)) {
+ char *value;
+ guint value_count;
+ GVariantBuilder *eb;
+ if (list_max > 0 && count == list_max) {
+ break;
+ }
+
+ if (list_offset > 0) {
+ list_offset--;
+ continue;
+ }
+
+ gtk_tree_model_get (model, &iter,
+ RHYTHMDB_PROPERTY_MODEL_COLUMN_TITLE, &value,
+ RHYTHMDB_PROPERTY_MODEL_COLUMN_NUMBER, &value_count,
+ -1);
+
+ eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+ if (all_props || rb_str_in_strv ("Parent", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Parent", g_variant_new_object_path (object_path));
+ }
+ if (all_props || rb_str_in_strv ("Type", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Type", g_variant_new_string ("container"));
+ }
+ if (all_props || rb_str_in_strv ("Path", filter)) {
+ char *encoded;
+ char *value_path;
+ encoded = encode_property_value (value);
+ value_path = g_strdup_printf ("%s/%s", object_path, encoded);
+ g_variant_builder_add (eb, "{sv}", "Path", g_variant_new_string (value_path));
+ g_free (encoded);
+ g_free (value_path);
+ }
+ if (all_props || rb_str_in_strv ("DisplayName", filter)) {
+ g_variant_builder_add (eb, "{sv}", "DisplayName", g_variant_new_string (value));
+ }
+ if (all_props || rb_str_in_strv ("ChildCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ChildCount", g_variant_new_uint32 (value_count));
+ }
+ if (all_props || rb_str_in_strv ("ItemCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ItemCount", g_variant_new_uint32 (value_count));
+ }
+ if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
+ }
+ if (all_props || rb_str_in_strv ("Searchable", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
+ }
+
+ g_variant_builder_add (list, "a{sv}", eb);
+ g_free (value);
+ count++;
+ }
+ }
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
+ g_variant_builder_unref (list);
+
+ g_strfreev ((char **)filter);
+ } else if (g_strcmp0 (method_name, "ListItems") == 0) {
+ list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
+ g_variant_builder_unref (list);
+ } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Method %s.%s not supported",
+ interface_name,
+ method_name);
+ }
+}
+
+static GVariant *
+get_property_container_property (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *property_name,
+ GError **error,
+ SourcePropertyRegistrationData *data)
+{
+ if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
+ if (g_strcmp0 (property_name, "Parent") == 0) {
+ return g_variant_new_object_path (data->source_data->dbus_path);
+ } else if (g_strcmp0 (property_name, "Type") == 0) {
+ return g_variant_new_string ("container");
+ } else if (g_strcmp0 (property_name, "Path") == 0) {
+ return g_variant_new_string (object_path);
+ } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
+ return g_variant_new_string (data->display_name);
+ }
+ } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
+
+ if (g_strcmp0 (property_name, "ChildCount") == 0 ||
+ g_strcmp0 (property_name, "ContainerCount") == 0) {
+ /* don't include the 'all' row */
+ guint count;
+ count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (data->model), NULL) - 1;
+ return g_variant_new_uint32 (count);
+ } else if (g_strcmp0 (property_name, "ItemCount") == 0) {
+ return g_variant_new_uint32 (0);
+ } else if (g_strcmp0 (property_name, "Searchable") == 0) {
+ return g_variant_new_boolean (FALSE);
+ }
+ }
+ g_set_error (error,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Property %s.%s not supported",
+ interface_name,
+ property_name);
+ return NULL;
+}
+
+static const GDBusInterfaceVTable property_vtable =
+{
+ (GDBusInterfaceMethodCallFunc) property_container_method_call,
+ (GDBusInterfaceGetPropertyFunc) get_property_container_property,
+ NULL
+};
+
+static void
+prop_model_row_inserted_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ SourcePropertyRegistrationData *prop_data)
+{
+ prop_data->updated = TRUE;
+ emit_updated_in_idle (prop_data->source_data->plugin);
+}
+
+static void
+prop_model_row_changed_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ SourcePropertyRegistrationData *prop_data)
+{
+ char *value;
+ RBRefString *refstring;
+ GList *l;
+
+ gtk_tree_model_get (model, iter,
+ RHYTHMDB_PROPERTY_MODEL_COLUMN_TITLE, &value,
+ -1);
+ refstring = rb_refstring_new (value);
+ g_free (value);
+
+ for (l = prop_data->updated_values; l != NULL; l = l->next) {
+ if (refstring == (RBRefString *)l->data) {
+ return;
+ }
+ }
+
+ prop_data->updated_values = g_list_prepend (prop_data->updated_values, refstring);
+ prop_data->updated = TRUE;
+ emit_updated_in_idle (prop_data->source_data->plugin);
+}
+
+static void
+prop_model_row_deleted_cb (RhythmDBPropertyModel *prop_model,
+ GtkTreePath *path,
+ SourcePropertyRegistrationData *prop_data)
+{
+ prop_data->updated = TRUE;
+ emit_updated_in_idle (prop_data->source_data->plugin);
+}
+
+static void
+disconnect_property_query_model_signals (SourcePropertyRegistrationData *prop_data, RhythmDBQueryModel *query_model)
+{
+ g_signal_handlers_disconnect_by_func (query_model, prop_model_row_inserted_cb, prop_data);
+ g_signal_handlers_disconnect_by_func (query_model, prop_model_row_changed_cb, prop_data);
+ g_signal_handlers_disconnect_by_func (query_model, prop_model_row_deleted_cb, prop_data);
}
-static const GDBusInterfaceVTable entry_vtable =
+static void
+connect_property_query_model_signals (SourcePropertyRegistrationData *prop_data, RhythmDBQueryModel *query_model)
{
- NULL,
- (GDBusInterfaceGetPropertyFunc) get_entry_property,
- NULL
-};
+ g_signal_connect (query_model, "row-inserted", G_CALLBACK (prop_model_row_inserted_cb), prop_data);
+ g_signal_connect (query_model, "row-changed", G_CALLBACK (prop_model_row_changed_cb), prop_data);
+ g_signal_connect (query_model, "row-deleted", G_CALLBACK (prop_model_row_deleted_cb), prop_data);
+}
+
static char **
-enumerate_entry_subtree (GDBusConnection *connection,
- const char *sender,
- const char *object_path,
- RBMediaServer2Plugin *plugin)
+enumerate_property_value_subtree (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ SourcePropertyRegistrationData *data)
{
return (char **)g_new0(char *, 1);
}
static GDBusInterfaceInfo **
-introspect_entry_subtree (GDBusConnection *connection,
- const char *sender,
- const char *object_path,
- const char *node,
- RBMediaServer2Plugin *plugin)
+introspect_property_value_subtree (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *node,
+ SourcePropertyRegistrationData *data)
{
GPtrArray *p;
GDBusInterfaceInfo *i;
p = g_ptr_array_new ();
- i = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
+ i = g_dbus_node_info_lookup_interface (data->source_data->plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
g_ptr_array_add (p, g_dbus_interface_info_ref (i));
- i = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_ITEM_IFACE_NAME);
+ i = g_dbus_node_info_lookup_interface (data->source_data->plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
g_ptr_array_add (p, g_dbus_interface_info_ref (i));
g_ptr_array_add (p, NULL);
@@ -365,96 +1084,90 @@ introspect_entry_subtree (GDBusConnection *connection,
}
static const GDBusInterfaceVTable *
-dispatch_entry_subtree (GDBusConnection *connection,
- const char *sender,
- const char *object_path,
- const char *interface_name,
- const char *node,
- gpointer *out_user_data,
- RBMediaServer2Plugin *plugin)
+dispatch_property_value_subtree (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *node,
+ gpointer *out_user_data,
+ SourcePropertyRegistrationData *data)
{
- *out_user_data = plugin;
- return &entry_vtable;
+ *out_user_data = data;
+ return &property_value_vtable;
}
-static GDBusSubtreeVTable entry_subtree_vtable =
+static const GDBusSubtreeVTable property_subtree_vtable =
{
- (GDBusSubtreeEnumerateFunc) enumerate_entry_subtree,
- (GDBusSubtreeIntrospectFunc) introspect_entry_subtree,
- (GDBusSubtreeDispatchFunc) dispatch_entry_subtree
+ (GDBusSubtreeEnumerateFunc) enumerate_property_value_subtree,
+ (GDBusSubtreeIntrospectFunc) introspect_property_value_subtree,
+ (GDBusSubtreeDispatchFunc) dispatch_property_value_subtree
};
-/* containers in general */
-
static void
-emit_updated (GDBusConnection *connection, const char *path)
-{
- GError *error = NULL;
- g_dbus_connection_emit_signal (connection,
- NULL,
- path,
- MEDIA_SERVER2_CONTAINER_IFACE_NAME,
- "Updated",
- NULL,
- &error);
- if (error != NULL) {
- g_warning ("Unable to emit Updated signal for MediaServer2 container %s: %s",
- path,
- error->message);
- g_clear_error (&error);
- }
-}
-
-static gboolean
-emit_container_updated_cb (RBMediaServer2Plugin *plugin)
+register_property_container (GDBusConnection *connection,
+ SourceRegistrationData *source_data,
+ RhythmDBPropType property,
+ const char *display_name)
{
- GList *l;
-
- rb_debug ("emitting updates");
- /* source containers */
- for (l = plugin->sources; l != NULL; l = l->next) {
- SourceRegistrationData *source_data = l->data;
- if (source_data->updated) {
- emit_source_property_updates (plugin, source_data);
- emit_updated (plugin->connection, source_data->dbus_path);
- source_data->updated = FALSE;
- }
- }
-
- /* source categories */
- for (l = plugin->categories; l != NULL; l = l->next) {
- CategoryRegistrationData *category_data = l->data;
- if (category_data->updated) {
- emit_category_container_property_updates (plugin, category_data);
- emit_updated (plugin->connection, category_data->dbus_path);
- category_data->updated = FALSE;
- }
- }
-
- /* root */
- if (plugin->root_updated) {
- emit_root_property_updates (plugin);
- emit_updated (plugin->connection, RB_MEDIASERVER2_ROOT);
- plugin->root_updated = FALSE;
- }
-
- rb_debug ("done emitting updates");
- plugin->emit_updated_id = 0;
- return FALSE;
+ SourcePropertyRegistrationData *data;
+ GDBusInterfaceInfo *iface;
+
+ data = g_new0 (SourcePropertyRegistrationData, 1);
+ data->source_data = source_data;
+ data->property = property;
+ data->display_name = g_strdup (display_name);
+ data->dbus_path = g_strdup_printf ("%s/%s",
+ source_data->dbus_path,
+ rhythmdb_nice_elt_name_from_propid (source_data->plugin->db, property));
+
+ data->model = rhythmdb_property_model_new (source_data->plugin->db, property);
+ g_object_set (data->model, "query-model", source_data->base_query_model, NULL);
+ connect_property_query_model_signals (data, source_data->base_query_model);
+
+ data->dbus_subtree_id =
+ g_dbus_connection_register_subtree (connection,
+ data->dbus_path,
+ &property_subtree_vtable,
+ G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
+ data,
+ NULL,
+ NULL);
+
+ iface = g_dbus_node_info_lookup_interface (source_data->plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
+ data->dbus_object_id[0] =
+ g_dbus_connection_register_object (connection,
+ data->dbus_path,
+ iface,
+ &property_vtable,
+ data,
+ NULL,
+ NULL);
+
+ iface = g_dbus_node_info_lookup_interface (source_data->plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
+ data->dbus_object_id[1] =
+ g_dbus_connection_register_object (connection,
+ data->dbus_path,
+ iface,
+ &property_vtable,
+ data,
+ NULL,
+ NULL);
+
+ source_data->properties = g_list_append (source_data->properties, data);
}
/* source containers */
static void
-source_method_call (GDBusConnection *connection,
- const char *sender,
- const char *object_path,
- const char *interface_name,
- const char *method_name,
- GVariant *parameters,
- GDBusMethodInvocation *invocation,
- SourceRegistrationData *source_data)
+source_tracks_method_call (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ SourceRegistrationData *source_data)
{
GVariantBuilder *list;
@@ -503,7 +1216,7 @@ source_method_call (GDBusConnection *connection,
eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
for (i = 0; filter[i] != NULL; i++) {
GVariant *v;
- v = get_entry_property_value (entry, filter[i]);
+ v = get_entry_property_value (entry, filter[i], RHYTHMDB_PROP_ENTRY_ID);
if (v != NULL) {
g_variant_builder_add (eb, "{sv}", filter[i], v);
}
@@ -535,29 +1248,37 @@ source_method_call (GDBusConnection *connection,
}
static GVariant *
-get_source_property (GDBusConnection *connection,
- const char *sender,
- const char *object_path,
- const char *interface_name,
- const char *property_name,
- GError **error,
- SourceRegistrationData *source_data)
+get_source_tracks_property (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *property_name,
+ GError **error,
+ SourceRegistrationData *source_data)
{
GVariant *v;
char *name;
if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
if (g_strcmp0 (property_name, "Parent") == 0) {
- return g_variant_new_object_path (source_data->parent_dbus_path);
+ if (source_data->flat) {
+ return g_variant_new_object_path (source_data->parent_dbus_path);
+ } else {
+ return g_variant_new_object_path (source_data->dbus_path);
+ }
} else if (g_strcmp0 (property_name, "Type") == 0) {
return g_variant_new_string ("container");
} else if (g_strcmp0 (property_name, "Path") == 0) {
return g_variant_new_string (object_path);
} else if (g_strcmp0 (property_name, "DisplayName") == 0) {
- g_object_get (source_data->source, "name", &name, NULL);
- v = g_variant_new_string (name);
- g_free (name);
- return v;
+ if (source_data->flat) {
+ g_object_get (source_data->source, "name", &name, NULL);
+ v = g_variant_new_string (name);
+ g_free (name);
+ return v;
+ } else {
+ return g_variant_new_string (_("All Tracks"));
+ }
}
} else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
@@ -582,15 +1303,15 @@ get_source_property (GDBusConnection *connection,
}
static void
-add_source_property (RBMediaServer2Plugin *plugin, GVariantBuilder *properties, const char *iface, const char *property, SourceRegistrationData *source_data)
+add_source_tracks_property (RBMediaServer2Plugin *plugin, GVariantBuilder *properties, const char *iface, const char *property, SourceRegistrationData *source_data)
{
GVariant *v;
- v = get_source_property (plugin->connection, NULL, source_data->dbus_path, iface, property, NULL, source_data);
+ v = get_source_tracks_property (plugin->connection, NULL, source_data->dbus_path, iface, property, NULL, source_data);
g_variant_builder_add (properties, "{sv}", property, v);
}
static void
-emit_source_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data)
+emit_source_tracks_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data)
{
GError *error = NULL;
const char *invalidated[] = { NULL };
@@ -599,9 +1320,9 @@ emit_source_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationDa
rb_debug ("updating properties for source %s", source_data->dbus_path);
properties = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
- add_source_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ItemCount", source_data);
- add_source_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ChildCount", source_data);
- add_source_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ContainerCount", source_data);
+ add_source_tracks_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ItemCount", source_data);
+ add_source_tracks_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ChildCount", source_data);
+ add_source_tracks_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ContainerCount", source_data);
parameters = g_variant_new ("(sa{sv}^as)",
MEDIA_SERVER2_CONTAINER_IFACE_NAME,
@@ -623,10 +1344,198 @@ emit_source_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationDa
}
}
-static const GDBusInterfaceVTable source_vtable =
+static const GDBusInterfaceVTable source_tracks_vtable =
+{
+ (GDBusInterfaceMethodCallFunc) source_tracks_method_call,
+ (GDBusInterfaceGetPropertyFunc) get_source_tracks_property,
+ NULL
+};
+
+
+static void
+source_properties_method_call (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ SourceRegistrationData *source_data)
+{
+ GVariantBuilder *list;
+ guint value_count;
+
+ if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) != 0) {
+ rb_debug ("method call on unexpected interface %s", interface_name);
+ return;
+ }
+
+ if (g_strcmp0 (method_name, "ListChildren") == 0 ||
+ g_strcmp0 (method_name, "ListContainers") == 0) {
+ GList *l;
+ guint list_offset;
+ guint list_max;
+ const char **filter;
+ guint count = 0;
+ gboolean all_props;
+ GVariantBuilder *eb;
+
+ g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
+ list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
+
+ all_props = rb_str_in_strv ("*", filter);
+
+ /* 'all tracks' container */
+ if (list_offset == 0) {
+ eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+ value_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source_data->base_query_model), NULL);
+ if (all_props || rb_str_in_strv ("Parent", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Parent", g_variant_new_object_path (object_path));
+ }
+ if (all_props || rb_str_in_strv ("Type", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Type", g_variant_new_string ("container"));
+ }
+ if (all_props || rb_str_in_strv ("Path", filter)) {
+ char *path;
+ path = g_strdup_printf ("%s/all", object_path);
+ g_variant_builder_add (eb, "{sv}", "Path", g_variant_new_string (path));
+ g_free (path);
+ }
+ if (all_props || rb_str_in_strv ("DisplayName", filter)) {
+ g_variant_builder_add (eb, "{sv}", "DisplayName", g_variant_new_string (_("All Tracks")));
+ }
+ if (all_props || rb_str_in_strv ("ChildCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ChildCount", g_variant_new_uint32 (value_count));
+ }
+ if (all_props || rb_str_in_strv ("ItemCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ItemCount", g_variant_new_uint32 (value_count));
+ }
+ if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
+ }
+ if (all_props || rb_str_in_strv ("Searchable", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
+ }
+
+ g_variant_builder_add (list, "a{sv}", eb);
+ count++;
+ } else {
+ list_offset--;
+ }
+
+ /* property-based containers */
+ for (l = source_data->properties; l != NULL; l = l->next) {
+ SourcePropertyRegistrationData *prop_data = l->data;
+ if (list_max > 0 && count == list_max) {
+ break;
+ }
+
+ if (list_offset > 0) {
+ list_offset--;
+ continue;
+ }
+
+ value_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (prop_data->model), NULL) - 1;
+
+ eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+ if (all_props || rb_str_in_strv ("Parent", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Parent", g_variant_new_object_path (object_path));
+ }
+ if (all_props || rb_str_in_strv ("Type", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Type", g_variant_new_string ("container"));
+ }
+ if (all_props || rb_str_in_strv ("Path", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Path", g_variant_new_string (prop_data->dbus_path));
+ }
+ if (all_props || rb_str_in_strv ("DisplayName", filter)) {
+ g_variant_builder_add (eb, "{sv}", "DisplayName", g_variant_new_string (prop_data->display_name));
+ }
+ if (all_props || rb_str_in_strv ("ChildCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ChildCount", g_variant_new_uint32 (value_count));
+ }
+ if (all_props || rb_str_in_strv ("ItemCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ItemCount", g_variant_new_uint32 (value_count));
+ }
+ if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
+ g_variant_builder_add (eb, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
+ }
+ if (all_props || rb_str_in_strv ("Searchable", filter)) {
+ g_variant_builder_add (eb, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
+ }
+
+ g_variant_builder_add (list, "a{sv}", eb);
+ count++;
+ }
+
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
+ g_variant_builder_unref (list);
+
+ g_strfreev ((char **)filter);
+ } else if (g_strcmp0 (method_name, "ListItems") == 0) {
+ list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
+ g_variant_builder_unref (list);
+ } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Method %s.%s not supported",
+ interface_name,
+ method_name);
+ }
+}
+
+static GVariant *
+get_source_properties_property (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *property_name,
+ GError **error,
+ SourceRegistrationData *source_data)
+{
+ GVariant *v;
+ char *name;
+
+ if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
+ if (g_strcmp0 (property_name, "Parent") == 0) {
+ return g_variant_new_object_path (source_data->parent_dbus_path);
+ } else if (g_strcmp0 (property_name, "Type") == 0) {
+ return g_variant_new_string ("container");
+ } else if (g_strcmp0 (property_name, "Path") == 0) {
+ return g_variant_new_string (object_path);
+ } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
+ g_object_get (source_data->source, "name", &name, NULL);
+ v = g_variant_new_string (name);
+ g_free (name);
+ return v;
+ }
+ } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
+
+ if (g_strcmp0 (property_name, "ChildCount") == 0 ||
+ g_strcmp0 (property_name, "ContainerCount") == 0) {
+ return g_variant_new_uint32 (g_list_length (source_data->properties) + 1);
+ } else if (g_strcmp0 (property_name, "ItemCount") == 0) {
+ return g_variant_new_uint32 (0);
+ } else if (g_strcmp0 (property_name, "Searchable") == 0) {
+ return g_variant_new_boolean (FALSE);
+ }
+ }
+ g_set_error (error,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Property %s.%s not supported",
+ interface_name,
+ property_name);
+ return NULL;
+}
+
+static const GDBusInterfaceVTable source_properties_vtable =
{
- (GDBusInterfaceMethodCallFunc) source_method_call,
- (GDBusInterfaceGetPropertyFunc) get_source_property,
+ (GDBusInterfaceMethodCallFunc) source_properties_method_call,
+ (GDBusInterfaceGetPropertyFunc) get_source_properties_property,
NULL
};
@@ -636,13 +1545,11 @@ static void
add_source_container (GVariantBuilder *list, SourceRegistrationData *source_data, const char **filter)
{
GVariantBuilder *i;
- int entry_count;
gboolean all_props;
i = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
all_props = rb_str_in_strv ("*", filter);
- entry_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source_data->base_query_model), NULL);
if (all_props || rb_str_in_strv ("Parent", filter)) {
g_variant_builder_add (i, "{sv}", "Parent", g_variant_new_object_path (source_data->parent_dbus_path));
@@ -659,14 +1566,29 @@ add_source_container (GVariantBuilder *list, SourceRegistrationData *source_data
g_variant_builder_add (i, "{sv}", "DisplayName", g_variant_new_string (name));
g_free (name);
}
- if (all_props || rb_str_in_strv ("ChildCount", filter)) {
- g_variant_builder_add (i, "{sv}", "ChildCount", g_variant_new_uint32 (entry_count));
- }
- if (all_props || rb_str_in_strv ("ItemCount", filter)) {
- g_variant_builder_add (i, "{sv}", "ItemCount", g_variant_new_uint32 (entry_count));
- }
- if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
- g_variant_builder_add (i, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
+ if (source_data->flat) {
+ int entry_count;
+ entry_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source_data->base_query_model), NULL);
+ if (all_props || rb_str_in_strv ("ChildCount", filter)) {
+ g_variant_builder_add (i, "{sv}", "ChildCount", g_variant_new_uint32 (entry_count));
+ }
+ if (all_props || rb_str_in_strv ("ItemCount", filter)) {
+ g_variant_builder_add (i, "{sv}", "ItemCount", g_variant_new_uint32 (entry_count));
+ }
+ if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
+ g_variant_builder_add (i, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
+ }
+ } else {
+ int child_count = g_list_length (source_data->properties) + 1;
+ if (all_props || rb_str_in_strv ("ChildCount", filter)) {
+ g_variant_builder_add (i, "{sv}", "ChildCount", g_variant_new_uint32 (child_count));
+ }
+ if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
+ g_variant_builder_add (i, "{sv}", "ContainerCount", g_variant_new_uint32 (child_count));
+ }
+ if (all_props || rb_str_in_strv ("ItemCount", filter)) {
+ g_variant_builder_add (i, "{sv}", "ItemCount", g_variant_new_uint32 (0));
+ }
}
if (all_props || rb_str_in_strv ("Searchable", filter)) {
g_variant_builder_add (i, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
@@ -764,10 +1686,7 @@ source_updated (SourceRegistrationData *source_data, gboolean count_changed)
}
}
- if (source_data->plugin->emit_updated_id == 0) {
- source_data->plugin->emit_updated_id =
- g_idle_add ((GSourceFunc)emit_container_updated_cb, source_data->plugin);
- }
+ emit_updated_in_idle (source_data->plugin);
}
/* signal handlers for source container updates */
@@ -786,13 +1705,33 @@ row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, SourceRegistrationData *
static void
entry_prop_changed_cb (RhythmDBQueryModel *model,
+ RhythmDBEntry *entry,
RhythmDBPropType prop,
const GValue *old,
const GValue *new_value,
SourceRegistrationData *source_data)
{
- if (entry_property_maps (prop)) {
- source_updated (source_data, FALSE);
+ GList *l;
+
+ if (entry_property_maps (prop) == FALSE) {
+ return;
+ }
+
+ source_updated (source_data, FALSE);
+ for (l = source_data->properties; l != NULL; l = l->next) {
+ SourcePropertyRegistrationData *prop_data = l->data;
+ RBRefString *value;
+
+ /* property model signal handlers will take care of this */
+ if (prop == prop_data->property)
+ continue;
+
+ prop_data->updated = TRUE;
+ value = rhythmdb_entry_get_refstring (entry, prop);
+ if (g_list_find (prop_data->updated_values, value) == NULL) {
+ prop_data->updated_values =
+ g_list_prepend (prop_data->updated_values, value);
+ }
}
}
@@ -815,7 +1754,14 @@ connect_query_model_signals (SourceRegistrationData *source_data)
static void
base_query_model_updated_cb (RBSource *source, GParamSpec *pspec, SourceRegistrationData *source_data)
{
+ GList *l;
+
if (source_data->base_query_model != NULL) {
+ for (l = source_data->properties; l != NULL; l = l->next) {
+ SourcePropertyRegistrationData *prop_data = l->data;
+ disconnect_property_query_model_signals (prop_data, source_data->base_query_model);
+ }
+
disconnect_query_model_signals (source_data);
g_object_unref (source_data->base_query_model);
}
@@ -823,6 +1769,11 @@ base_query_model_updated_cb (RBSource *source, GParamSpec *pspec, SourceRegistra
g_object_get (source, "base-query-model", &source_data->base_query_model, NULL);
connect_query_model_signals (source_data);
+ for (l = source_data->properties; l != NULL; l = l->next) {
+ SourcePropertyRegistrationData *prop_data = l->data;
+ connect_property_query_model_signals (prop_data, source_data->base_query_model);
+ }
+
source_updated (source_data, TRUE);
}
@@ -845,11 +1796,12 @@ source_deleted_cb (RBDisplayPage *page, RBMediaServer2Plugin *plugin)
}
-static void
+static SourceRegistrationData *
register_source_container (RBMediaServer2Plugin *plugin,
RBSource *source,
const char *dbus_path,
- const char *parent_dbus_path)
+ const char *parent_dbus_path,
+ gboolean flat)
{
SourceRegistrationData *source_data;
GDBusInterfaceInfo *container_iface;
@@ -859,9 +1811,19 @@ register_source_container (RBMediaServer2Plugin *plugin,
source_data->dbus_path = g_strdup (dbus_path);
source_data->parent_dbus_path = g_strdup (parent_dbus_path);
source_data->plugin = plugin;
+ source_data->flat = flat;
container_iface = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
- register_object (plugin, &source_vtable, container_iface, dbus_path, source_data, source_data->dbus_reg_id);
+ if (flat) {
+ register_object (plugin, &source_tracks_vtable, container_iface, dbus_path, source_data, source_data->dbus_reg_id);
+ } else {
+ char *tracks_path;
+
+ register_object (plugin, &source_properties_vtable, container_iface, dbus_path, source_data, source_data->dbus_reg_id);
+
+ tracks_path = g_strdup_printf ("%s/all", dbus_path);
+ register_object (plugin, &source_tracks_vtable, container_iface, tracks_path, source_data, source_data->all_tracks_reg_id);
+ }
g_object_get (source, "base-query-model", &source_data->base_query_model, NULL);
connect_query_model_signals (source_data);
@@ -874,6 +1836,8 @@ register_source_container (RBMediaServer2Plugin *plugin,
/* emit 'updated' signal on parent container */
g_dbus_connection_emit_signal (plugin->connection, NULL, parent_dbus_path, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "Updated", NULL, NULL);
+
+ return source_data;
}
static void
@@ -1369,7 +2333,7 @@ display_page_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *i
-1);
if (RB_IS_SOURCE (page)) {
RBSource *source = RB_SOURCE (page);
- /* figure out if this is a source can publish */
+ /* figure out if this is a source we can publish */
for (l = plugin->categories; l != NULL; l = l->next) {
CategoryRegistrationData *category_data = l->data;
@@ -1379,7 +2343,7 @@ display_page_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *i
category_data->dbus_path,
(gintptr) source);
rb_debug ("adding new source %s to category %s", dbus_path, category_data->name);
- register_source_container (plugin, source, dbus_path, category_data->dbus_path);
+ register_source_container (plugin, source, dbus_path, category_data->dbus_path, TRUE);
g_free (dbus_path);
}
}
@@ -1408,6 +2372,7 @@ impl_activate (PeasActivatable *bplugin)
{
RBMediaServer2Plugin *plugin;
GDBusInterfaceInfo *container_iface;
+ SourceRegistrationData *source_data;
RBSource *source;
GError *error = NULL;
RBShell *shell;
@@ -1446,7 +2411,11 @@ impl_activate (PeasActivatable *bplugin)
/* register fixed sources (library, podcasts, etc.) */
g_object_get (shell, "library-source", &source, NULL);
- register_source_container (plugin, source, RB_MEDIASERVER2_LIBRARY, RB_MEDIASERVER2_ROOT);
+ source_data = register_source_container (plugin, source, RB_MEDIASERVER2_LIBRARY, RB_MEDIASERVER2_ROOT, FALSE);
+ register_property_container (plugin->connection, source_data, RHYTHMDB_PROP_ARTIST, _("Artists"));
+ register_property_container (plugin->connection, source_data, RHYTHMDB_PROP_ALBUM, _("Albums"));
+ register_property_container (plugin->connection, source_data, RHYTHMDB_PROP_GENRE, _("Genres"));
+ /* year won't work yet */
g_object_unref (source);
/* watch for user-creatable sources (playlists, devices) */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]