No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | rb-dbus-media-server-plugin.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * rb-dbus-media-server-plugin.c
3 *
4 * Copyright (C) 2010 Jonathan Matthew <jonathan@d14n.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * The Rhythmbox authors hereby grant permission for non-GPL compatible
12 * GStreamer plugins to be used and distributed together with GStreamer
13 * and Rhythmbox. This permission is above and beyond the permissions granted
14 * by the GPL license by which Rhythmbox is covered. If you modify this code
15 * you may extend this exception to your version of the code, but you are not
16 * obligated to do so. If you do not wish to do so, delete this exception
17 * statement from your version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 */
28
29 #include <config.h>
30
31 #include <string.h>
32 #include <glib/gi18n-lib.h>
33 #include <gmodule.h>
34 #include <gtk/gtk.h>
35 #include <glib.h>
36 #include <glib-object.h>
37 #include <gio/gio.h>
38
39 #include <lib/rb-util.h>
40 #include <lib/rb-debug.h>
41 #include <lib/rb-gst-media-types.h>
42 #include <plugins/rb-plugin-macros.h>
43 #include <shell/rb-shell.h>
44 #include <shell/rb-shell-player.h>
45 #include <sources/rb-display-page-model.h>
46 #include <sources/rb-playlist-source.h>
47 #include <sources/rb-device-source.h>
48 #include <rhythmdb/rhythmdb-property-model.h>
49
50 #define RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN (rb_dbus_media_server_plugin_get_type ())
51 #define RB_DBUS_MEDIA_SERVER_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN, RBMediaServer2Plugin))
52 #define RB_DBUS_MEDIA_SERVER_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN, RBMediaServer2PluginClass))
53 #define RB_IS_DBUS_MEDIA_SERVER_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN))
54 #define RB_IS_DBUS_MEDIA_SERVER_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN))
55 #define RB_DBUS_MEDIA_SERVER_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN, RBMediaServer2PluginClass))
56
57 #include "dbus-media-server-spec.h"
58
59 #define RB_MEDIASERVER2_BUS_NAME MEDIA_SERVER2_BUS_NAME_PREFIX ".Rhythmbox"
60
61 #define RB_MEDIASERVER2_PREFIX "/org/gnome/UPnP/MediaServer2/"
62 #define RB_MEDIASERVER2_ROOT RB_MEDIASERVER2_PREFIX "Rhythmbox"
63 #define RB_MEDIASERVER2_LIBRARY RB_MEDIASERVER2_PREFIX "Library"
64 #define RB_MEDIASERVER2_PLAYLISTS RB_MEDIASERVER2_PREFIX "Playlists"
65 #define RB_MEDIASERVER2_DEVICES RB_MEDIASERVER2_PREFIX "Devices"
66 #define RB_MEDIASERVER2_ENTRY_SUBTREE RB_MEDIASERVER2_PREFIX "Entry"
67 #define RB_MEDIASERVER2_ENTRY_PREFIX RB_MEDIASERVER2_ENTRY_SUBTREE "/"
68
69 typedef struct
70 {
71 PeasExtensionBase parent;
72
73 GDBusNodeInfo *node_info;
74 guint name_own_id;
75
76 GDBusConnection *connection;
77
78 /* object/subtree registration ids */
79 guint root_reg_id[2];
80 gboolean root_updated;
81 guint entry_reg_id;
82
83 guint emit_updated_id;
84
85 /* source and category registrations */
86 GList *sources;
87 GList *categories;
88
89 GSettings *settings;
90 RhythmDB *db;
91 RBDisplayPageModel *display_page_model;
92 } RBMediaServer2Plugin;
93
94 typedef struct
95 {
96 PeasExtensionBaseClass parent_class;
97 } RBMediaServer2PluginClass;
98
99 typedef struct
100 {
101 char *name;
102 guint dbus_reg_id[2];
103 gboolean updated;
104 char *dbus_path;
105 char *parent_dbus_path;
106
107 gboolean (*match_source) (RBSource *source);
108
109 RBMediaServer2Plugin *plugin;
110 } CategoryRegistrationData;
111
112 typedef struct
113 {
114 RBSource *source;
115 RhythmDBQueryModel *base_query_model;
116 guint dbus_reg_id[2];
117 gboolean updated;
118 char *dbus_path;
119 char *parent_dbus_path;
120
121 gboolean flat;
122 guint all_tracks_reg_id[2];
123 GList *properties;
124
125 RBMediaServer2Plugin *plugin;
126 } SourceRegistrationData;
127
128 typedef struct
129 {
130 SourceRegistrationData *source_data;
131 char *dbus_path;
132 char *display_name;
133 guint dbus_object_id[2];
134 guint dbus_subtree_id;
135 RhythmDBPropType property;
136 RhythmDBPropertyModel *model;
137 gboolean updated;
138 GList *updated_values;
139 } SourcePropertyRegistrationData;
140
141 RB_DEFINE_PLUGIN(RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN, RBMediaServer2Plugin, rb_dbus_media_server_plugin,)
142
143 G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module);
144
145 static void unregister_source_container (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data, gboolean deactivating);
146 static void emit_source_tracks_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data);
147 static void emit_property_value_property_updates (RBMediaServer2Plugin *plugin, SourcePropertyRegistrationData *source_data, RBRefString *value);
148 static void emit_category_container_property_updates (RBMediaServer2Plugin *plugin, CategoryRegistrationData *category_data);
149 static void emit_root_property_updates (RBMediaServer2Plugin *plugin);
150
151 static void
152 rb_dbus_media_server_plugin_init (RBMediaServer2Plugin *plugin)
153 {
154 }
155
156 static void
157 register_object (RBMediaServer2Plugin *plugin,
158 const GDBusInterfaceVTable *vtable,
159 GDBusInterfaceInfo *iface_info,
160 const char *object_path,
161 gpointer method_data,
162 guint *ids)
163 {
164 GError *error = NULL;
165 GDBusInterfaceInfo *object_iface;
166 object_iface = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
167
168 ids[0] = g_dbus_connection_register_object (plugin->connection,
169 object_path,
170 object_iface,
171 vtable,
172 method_data,
173 NULL,
174 &error);
175 if (error != NULL) {
176 g_warning ("Unable to register MediaServer2 object %s: %s",
177 object_path,
178 error->message);
179 g_clear_error (&error);
180 }
181
182 ids[1] = g_dbus_connection_register_object (plugin->connection,
183 object_path,
184 iface_info,
185 vtable,
186 method_data,
187 NULL,
188 &error);
189 if (error != NULL) {
190 g_warning ("Unable to register MediaServer2 object %s: %s",
191 object_path,
192 error->message);
193 g_clear_error (&error);
194 }
195 }
196
197 static void
198 unregister_object (RBMediaServer2Plugin *plugin, guint *ids)
199 {
200 if (ids[0] != 0) {
201 g_dbus_connection_unregister_object (plugin->connection, ids[0]);
202 ids[0] = 0;
203 }
204 if (ids[1] != 0) {
205 g_dbus_connection_unregister_object (plugin->connection, ids[1]);
206 ids[1] = 0;
207 }
208 }
209
210 /* entry subtree */
211
212 char *all_entry_properties[] = {
213 "Parent",
214 "Type",
215 "Path",
216 "DisplayName",
217 "URLs",
218 "MIMEType",
219 "Size",
220 "Artist",
221 "Album",
222 "Date",
223 "Genre",
224 "DLNAProfile",
225 "Duration",
226 "Bitrate",
227 "AlbumArt",
228 "TrackNumber"
229 };
230
231 /* not used yet, since album art isn't exposed
232 static gboolean
233 entry_extra_metadata_maps (const char *extra_metadata)
234 {
235 if (g_strcmp0 (extra_metadata, RHYTHMDB_PROP_COVER_ART_URI) == 0) {
236 return TRUE;
237 }
238 return FALSE;
239 }
240 */
241
242 static gboolean
243 entry_property_maps (RhythmDBPropType prop)
244 {
245 switch (prop) {
246 case RHYTHMDB_PROP_TITLE:
247 case RHYTHMDB_PROP_MEDIA_TYPE:
248 case RHYTHMDB_PROP_FILE_SIZE:
249 case RHYTHMDB_PROP_ALBUM:
250 case RHYTHMDB_PROP_ARTIST:
251 case RHYTHMDB_PROP_YEAR:
252 case RHYTHMDB_PROP_GENRE:
253 case RHYTHMDB_PROP_DURATION:
254 case RHYTHMDB_PROP_BITRATE:
255 case RHYTHMDB_PROP_TRACK_NUMBER:
256 return TRUE;
257
258 default:
259 return FALSE;
260 }
261 }
262
263 static GVariant *
264 get_entry_property_value (RhythmDBEntry *entry, const char *property_name)
265 {
266 GVariant *v;
267
268 if (g_strcmp0 (property_name, "Parent") == 0) {
269 return g_variant_new_object_path (RB_MEDIASERVER2_ROOT);
270 } else if (g_strcmp0 (property_name, "Type") == 0) {
271 return g_variant_new_string ("music");
272 } else if (g_strcmp0 (property_name, "Path") == 0) {
273 char *path;
274
275 path = g_strdup_printf (RB_MEDIASERVER2_ENTRY_PREFIX "%lu",
276 rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_ENTRY_ID));
277 v = g_variant_new_string (path);
278 g_free (path);
279 return v;
280 } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
281 return g_variant_new_string (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
282 } else if (g_strcmp0 (property_name, "URLs") == 0) {
283 const char *urls[] = { NULL, NULL };
284 char *url;
285 url = rhythmdb_entry_get_playback_uri (entry);
286 urls[0] = url;
287 v = g_variant_new_strv (urls, -1);
288 g_free (url);
289 return v;
290
291 } else if (g_strcmp0 (property_name, "MIMEType") == 0) {
292 const char *media_type;
293 media_type = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MEDIA_TYPE);
294 return g_variant_new_string (rb_gst_media_type_to_mime_type (media_type));
295 } else if (g_strcmp0 (property_name, "Size") == 0) {
296 return g_variant_new_int64 (rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE));
297 } else if (g_strcmp0 (property_name, "Artist") == 0) {
298 return g_variant_new_string (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
299 } else if (g_strcmp0 (property_name, "Album") == 0) {
300 return g_variant_new_string (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
301 } else if (g_strcmp0 (property_name, "Date") == 0) {
302 char *iso8601;
303 iso8601 = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02dZ",
304 (int)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_YEAR),
305 1, 1, 0, 0, 0);
306 v = g_variant_new_string (iso8601);
307 g_free (iso8601);
308 return v;
309 } else if (g_strcmp0 (property_name, "Genre") == 0) {
310 return g_variant_new_string (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE));
311 } else if (g_strcmp0 (property_name, "Duration") == 0) {
312 return g_variant_new_int32 (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION));
313 } else if (g_strcmp0 (property_name, "Bitrate") == 0) {
314 return g_variant_new_int32 (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE));
315 } else if (g_strcmp0 (property_name, "TrackNumber") == 0) {
316 return g_variant_new_int32 (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
317 }
318
319 /* not yet: DLNAProfile, AlbumArt */
320
321 return NULL;
322 }
323
324 static GVariant *
325 get_entry_property (GDBusConnection *connection,
326 const char *sender,
327 const char *object_path,
328 const char *interface_name,
329 const char *property_name,
330 GError **error,
331 RBMediaServer2Plugin *plugin)
332 {
333 RhythmDBEntry *entry;
334
335 rb_debug ("entry property %s", property_name);
336 if (g_str_has_prefix (object_path, RB_MEDIASERVER2_ENTRY_PREFIX) == FALSE) {
337 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "no");
338 return NULL;
339 }
340
341 entry = rhythmdb_entry_lookup_from_string (plugin->db, object_path + strlen (RB_MEDIASERVER2_ENTRY_PREFIX), TRUE);
342 if (entry == NULL) {
343 rb_debug ("entry for object path %s not found", object_path);
344 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "no");
345 return NULL;
346 }
347
348 return get_entry_property_value (entry, property_name);
349 }
350
351 static const GDBusInterfaceVTable entry_vtable =
352 {
353 NULL,
354 (GDBusInterfaceGetPropertyFunc) get_entry_property,
355 NULL
356 };
357
358 static char **
359 enumerate_entry_subtree (GDBusConnection *connection,
360 const char *sender,
361 const char *object_path,
362 RBMediaServer2Plugin *plugin)
363 {
364 return (char **)g_new0(char *, 1);
365 }
366
367 static GDBusInterfaceInfo **
368 introspect_entry_subtree (GDBusConnection *connection,
369 const char *sender,
370 const char *object_path,
371 const char *node,
372 RBMediaServer2Plugin *plugin)
373 {
374 GPtrArray *p;
375 GDBusInterfaceInfo *i;
376
377 p = g_ptr_array_new ();
378
379 i = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
380 g_ptr_array_add (p, g_dbus_interface_info_ref (i));
381
382 i = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_ITEM_IFACE_NAME);
383 g_ptr_array_add (p, g_dbus_interface_info_ref (i));
384
385 g_ptr_array_add (p, NULL);
386
387 return (GDBusInterfaceInfo **)g_ptr_array_free (p, FALSE);
388 }
389
390 static const GDBusInterfaceVTable *
391 dispatch_entry_subtree (GDBusConnection *connection,
392 const char *sender,
393 const char *object_path,
394 const char *interface_name,
395 const char *node,
396 gpointer *out_user_data,
397 RBMediaServer2Plugin *plugin)
398 {
399 *out_user_data = plugin;
400 return &entry_vtable;
401 }
402
403 static GDBusSubtreeVTable entry_subtree_vtable =
404 {
405 (GDBusSubtreeEnumerateFunc) enumerate_entry_subtree,
406 (GDBusSubtreeIntrospectFunc) introspect_entry_subtree,
407 (GDBusSubtreeDispatchFunc) dispatch_entry_subtree
408 };
409
410 /* containers in general */
411
412 static void
413 emit_updated (GDBusConnection *connection, const char *path)
414 {
415 GError *error = NULL;
416 g_dbus_connection_emit_signal (connection,
417 NULL,
418 path,
419 MEDIA_SERVER2_CONTAINER_IFACE_NAME,
420 "Updated",
421 NULL,
422 &error);
423 if (error != NULL) {
424 g_warning ("Unable to emit Updated signal for MediaServer2 container %s: %s",
425 path,
426 error->message);
427 g_clear_error (&error);
428 }
429 }
430
431 static gboolean
432 emit_container_updated_cb (RBMediaServer2Plugin *plugin)
433 {
434 GList *l, *ll, *lll;
435
436 rb_debug ("emitting updates");
437 /* source containers */
438 for (l = plugin->sources; l != NULL; l = l->next) {
439 SourceRegistrationData *source_data = l->data;
440
441 /* property containers */
442 for (ll = source_data->properties; ll != NULL; ll = ll->next) {
443 SourcePropertyRegistrationData *prop_data = ll->data;
444
445 /* emit value updates */
446 for (lll = prop_data->updated_values; lll != NULL; lll = lll->next) {
447 RBRefString *value = lll->data;
448 emit_property_value_property_updates (plugin, prop_data, value);
449 }
450 rb_list_destroy_free (prop_data->updated_values, (GDestroyNotify)rb_refstring_unref);
451 prop_data->updated_values = NULL;
452
453 if (prop_data->updated) {
454 emit_updated (plugin->connection, prop_data->dbus_path);
455 prop_data->updated = FALSE;
456 }
457 }
458
459 if (source_data->updated) {
460 emit_source_tracks_property_updates (plugin, source_data);
461 if (source_data->flat) {
462 emit_updated (plugin->connection, source_data->dbus_path);
463 } else {
464 char *path;
465 path = g_strdup_printf ("%s/all", source_data->dbus_path);
466 emit_updated (plugin->connection, path);
467 g_free (path);
468 }
469 source_data->updated = FALSE;
470 }
471
472 }
473
474 /* source categories */
475 for (l = plugin->categories; l != NULL; l = l->next) {
476 CategoryRegistrationData *category_data = l->data;
477 if (category_data->updated) {
478 emit_category_container_property_updates (plugin, category_data);
479 emit_updated (plugin->connection, category_data->dbus_path);
480 category_data->updated = FALSE;
481 }
482 }
483
484 /* root */
485 if (plugin->root_updated) {
486 emit_root_property_updates (plugin);
487 emit_updated (plugin->connection, RB_MEDIASERVER2_ROOT);
488 plugin->root_updated = FALSE;
489 }
490
491 rb_debug ("done emitting updates");
492 plugin->emit_updated_id = 0;
493 return FALSE;
494 }
495
496 static void
497 emit_updated_in_idle (RBMediaServer2Plugin *plugin)
498 {
499 if (plugin->emit_updated_id == 0) {
500 plugin->emit_updated_id =
501 g_idle_add_full (G_PRIORITY_LOW,
502 (GSourceFunc)emit_container_updated_cb,
503 plugin,
504 NULL);
505 }
506 }
507
508
509 /* property value source subcontainers (source/year/1995) */
510
511 static char *
512 encode_property_value (const char *value)
513 {
514 char *encoded;
515 const char *hex = "0123456789ABCDEF";
516 char *d;
517 char c;
518
519 encoded = g_malloc0 (strlen (value) * 3 + 1);
520 d = encoded;
521 while (*value != '\0') {
522 c = *value++;
523 if (g_ascii_isalnum (c)) {
524 *d++ = c;
525 } else {
526 guint8 v = (guint8)c;
527 *d++ = '_';
528 *d++ = hex[(v >> 4) & 0x0f];
529 *d++ = hex[v & 0x0f];
530 }
531 }
532
533 return encoded;
534 }
535
536 #define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
537 #define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
538
539 static char *
540 decode_property_value (char *encoded)
541 {
542 char *decoded;
543 char *e;
544 char *d;
545
546 decoded = g_malloc0 (strlen (encoded) + 1);
547 d = decoded;
548 e = encoded;
549 while (*e != '\0') {
550 if (*e != '_') {
551 *d++ = *e++;
552 } else if (e[1] != '\0' && e[2] != '\0') {
553 *d++ = HEXCHAR(e);
554 e += 3;
555 } else {
556 /* broken */
557 break;
558 }
559 }
560
561 return decoded;
562 }
563
564 static char *
565 extract_property_value (RhythmDB *db, const char *object_path)
566 {
567 char **bits;
568 char *value;
569 int nbits;
570
571 bits = g_strsplit (object_path, "/", 0);
572 nbits = g_strv_length (bits);
573
574 value = decode_property_value (bits[nbits-1]);
575 g_strfreev (bits);
576 return value;
577 }
578
579 static void
580 property_value_method_call (GDBusConnection *connection,
581 const char *sender,
582 const char *object_path,
583 const char *interface_name,
584 const char *method_name,
585 GVariant *parameters,
586 GDBusMethodInvocation *invocation,
587 SourcePropertyRegistrationData *data)
588 {
589 GVariantBuilder *list;
590 RhythmDB *db;
591 char *value;
592
593 if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) != 0) {
594 rb_debug ("method call on unexpected interface %s", interface_name);
595 return;
596 }
597
598 db = data->source_data->plugin->db;
599 value = extract_property_value (db, object_path);
600
601 if (g_strcmp0 (method_name, "ListChildren") == 0 ||
602 g_strcmp0 (method_name, "ListItems") == 0) {
603 RhythmDBQuery *base;
604 RhythmDBQuery *query;
605 RhythmDBQueryModel *query_model;
606 GtkTreeModel *model;
607 GtkTreeIter iter;
608 guint list_offset;
609 guint list_max;
610 char **filter;
611 guint count = 0;
612
613 /* consider caching query models? */
614 g_object_get (data->source_data->base_query_model, "query", &base, NULL);
615 query = rhythmdb_query_copy (base);
616
617 rhythmdb_query_append (db,
618 query,
619 RHYTHMDB_QUERY_PROP_EQUALS, data->property, value,
620 RHYTHMDB_QUERY_END);
621 /* maybe use a result list? */
622 query_model = rhythmdb_query_model_new_empty (db);
623 rhythmdb_do_full_query_parsed (db, RHYTHMDB_QUERY_RESULTS (query_model), query);
624 rhythmdb_query_free (query);
625
626 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
627 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
628
629 if (rb_str_in_strv ("*", (const char **)filter)) {
630 g_strfreev (filter);
631 filter = g_strdupv ((char **)all_entry_properties);
632 }
633
634 model = GTK_TREE_MODEL (query_model);
635 if (gtk_tree_model_get_iter_first (model, &iter)) {
636 do {
637 RhythmDBEntry *entry;
638 GVariantBuilder *eb;
639 int i;
640 if (list_max > 0 && count == list_max) {
641 break;
642 }
643
644 entry = rhythmdb_query_model_iter_to_entry (query_model, &iter);
645 if (entry == NULL) {
646 continue;
647 }
648
649 if (list_offset > 0) {
650 list_offset--;
651 continue;
652 }
653
654 eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
655 for (i = 0; filter[i] != NULL; i++) {
656 GVariant *v;
657 v = get_entry_property_value (entry, filter[i]);
658 if (v != NULL) {
659 g_variant_builder_add (eb, "{sv}", filter[i], v);
660 }
661 }
662
663 g_variant_builder_add (list, "a{sv}", eb);
664 count++;
665
666 } while (gtk_tree_model_iter_next (model, &iter));
667 }
668 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
669 g_variant_builder_unref (list);
670
671 g_strfreev (filter);
672 } else if (g_strcmp0 (method_name, "ListContainers") == 0) {
673 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
674 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
675 g_variant_builder_unref (list);
676 } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
677 g_dbus_method_invocation_return_value (invocation, NULL);
678 } else {
679 g_dbus_method_invocation_return_error (invocation,
680 G_DBUS_ERROR,
681 G_DBUS_ERROR_NOT_SUPPORTED,
682 "Method %s.%s not supported",
683 interface_name,
684 method_name);
685 }
686
687 g_free (value);
688 }
689
690 static guint
691 get_property_value_count (SourcePropertyRegistrationData *data, const char *value)
692 {
693 guint entry_count = 0;
694 GtkTreeIter iter;
695
696 if (rhythmdb_property_model_iter_from_string (data->model, value, &iter)) {
697 gtk_tree_model_get (GTK_TREE_MODEL (data->model), &iter,
698 RHYTHMDB_PROPERTY_MODEL_COLUMN_NUMBER, &entry_count,
699 -1);
700 }
701 return entry_count;
702 }
703
704 static GVariant *
705 get_property_value_property (GDBusConnection *connection,
706 const char *sender,
707 const char *object_path,
708 const char *interface_name,
709 const char *property_name,
710 GError **error,
711 SourcePropertyRegistrationData *data)
712 {
713 RhythmDB *db;
714 GVariant *v = NULL;
715 char *value;
716
717 db = data->source_data->plugin->db;
718 value = extract_property_value (db, object_path);
719
720 if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
721 if (g_strcmp0 (property_name, "Parent") == 0) {
722 v = g_variant_new_object_path (data->dbus_path);
723 } else if (g_strcmp0 (property_name, "Type") == 0) {
724 v = g_variant_new_string ("container");
725 } else if (g_strcmp0 (property_name, "Path") == 0) {
726 v = g_variant_new_string (object_path);
727 } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
728 v = g_variant_new_string (value);
729 }
730 } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
731
732 if (g_strcmp0 (property_name, "ChildCount") == 0 ||
733 g_strcmp0 (property_name, "ItemCount") == 0) {
734 v = g_variant_new_uint32 (get_property_value_count (data, value));
735 } else if (g_strcmp0 (property_name, "ContainerCount") == 0) {
736 v = g_variant_new_uint32 (0);
737 } else if (g_strcmp0 (property_name, "Searchable") == 0) {
738 v = g_variant_new_boolean (FALSE);
739 }
740 }
741
742 if (v == NULL) {
743 g_set_error (error,
744 G_DBUS_ERROR,
745 G_DBUS_ERROR_NOT_SUPPORTED,
746 "Property %s.%s not supported",
747 interface_name,
748 property_name);
749 }
750 g_free (value);
751 return v;
752 }
753
754 static void
755 emit_property_value_property_updates (RBMediaServer2Plugin *plugin, SourcePropertyRegistrationData *data, RBRefString *value)
756 {
757 GError *error = NULL;
758 const char *invalidated[] = { NULL };
759 GVariantBuilder *properties;
760 GVariant *parameters;
761 GVariant *v;
762 char *encoded;
763 char *path;
764
765 rb_debug ("updating properties for %s/%s", data->dbus_path, rb_refstring_get (value));
766 properties = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
767
768 v = g_variant_new_uint32 (get_property_value_count (data, rb_refstring_get (value)));
769 g_variant_builder_add (properties, "{sv}", "ItemCount", v);
770 g_variant_builder_add (properties, "{sv}", "ChildCount", v);
771 g_variant_builder_add (properties, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
772
773 encoded = encode_property_value (rb_refstring_get (value));
774 path = g_strdup_printf ("%s/%s", data->dbus_path, encoded);
775 g_free (encoded);
776
777 parameters = g_variant_new ("(sa{sv}^as)",
778 MEDIA_SERVER2_CONTAINER_IFACE_NAME,
779 properties,
780 invalidated);
781 g_variant_builder_unref (properties);
782 g_dbus_connection_emit_signal (plugin->connection,
783 NULL,
784 path,
785 "org.freedesktop.DBus.Properties",
786 "PropertiesChanged",
787 parameters,
788 &error);
789 if (error != NULL) {
790 g_warning ("Unable to send property changes for MediaServer2 container %s: %s",
791 path,
792 error->message);
793 g_clear_error (&error);
794 }
795
796 emit_updated (plugin->connection, path);
797
798 g_free (path);
799 }
800
801 static const GDBusInterfaceVTable property_value_vtable =
802 {
803 (GDBusInterfaceMethodCallFunc) property_value_method_call,
804 (GDBusInterfaceGetPropertyFunc) get_property_value_property,
805 NULL
806 };
807
808 /* property-based source subcontainers (source/year/) */
809
810 static void
811 property_container_method_call (GDBusConnection *connection,
812 const char *sender,
813 const char *object_path,
814 const char *interface_name,
815 const char *method_name,
816 GVariant *parameters,
817 GDBusMethodInvocation *invocation,
818 SourcePropertyRegistrationData *data)
819 {
820 GVariantBuilder *list;
821
822 if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) != 0) {
823 rb_debug ("method call on unexpected interface %s", interface_name);
824 return;
825 }
826
827 if (g_strcmp0 (method_name, "ListChildren") == 0 ||
828 g_strcmp0 (method_name, "ListContainers") == 0) {
829 GtkTreeModel *model;
830 GtkTreeIter iter;
831 guint list_offset;
832 guint list_max;
833 const char **filter;
834 guint count = 0;
835 gboolean all_props;
836
837 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
838 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
839
840 all_props = rb_str_in_strv ("*", filter);
841
842 model = GTK_TREE_MODEL (data->model);
843 if (gtk_tree_model_get_iter_first (model, &iter)) {
844 /* skip 'all' row */
845 while (gtk_tree_model_iter_next (model, &iter)) {
846 char *value;
847 guint value_count;
848 GVariantBuilder *eb;
849 if (list_max > 0 && count == list_max) {
850 break;
851 }
852
853 if (list_offset > 0) {
854 list_offset--;
855 continue;
856 }
857
858 gtk_tree_model_get (model, &iter,
859 RHYTHMDB_PROPERTY_MODEL_COLUMN_TITLE, &value,
860 RHYTHMDB_PROPERTY_MODEL_COLUMN_NUMBER, &value_count,
861 -1);
862
863 eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
864 if (all_props || rb_str_in_strv ("Parent", filter)) {
865 g_variant_builder_add (eb, "{sv}", "Parent", g_variant_new_object_path (object_path));
866 }
867 if (all_props || rb_str_in_strv ("Type", filter)) {
868 g_variant_builder_add (eb, "{sv}", "Type", g_variant_new_string ("container"));
869 }
870 if (all_props || rb_str_in_strv ("Path", filter)) {
871 char *encoded;
872 char *value_path;
873 encoded = encode_property_value (value);
874 value_path = g_strdup_printf ("%s/%s", object_path, encoded);
875 g_variant_builder_add (eb, "{sv}", "Path", g_variant_new_string (value_path));
876 g_free (encoded);
877 g_free (value_path);
878 }
879 if (all_props || rb_str_in_strv ("DisplayName", filter)) {
880 g_variant_builder_add (eb, "{sv}", "DisplayName", g_variant_new_string (value));
881 }
882 if (all_props || rb_str_in_strv ("ChildCount", filter)) {
883 g_variant_builder_add (eb, "{sv}", "ChildCount", g_variant_new_uint32 (value_count));
884 }
885 if (all_props || rb_str_in_strv ("ItemCount", filter)) {
886 g_variant_builder_add (eb, "{sv}", "ItemCount", g_variant_new_uint32 (value_count));
887 }
888 if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
889 g_variant_builder_add (eb, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
890 }
891 if (all_props || rb_str_in_strv ("Searchable", filter)) {
892 g_variant_builder_add (eb, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
893 }
894
895 g_variant_builder_add (list, "a{sv}", eb);
896 g_free (value);
897 count++;
898 }
899 }
900 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
901 g_variant_builder_unref (list);
902
903 g_strfreev ((char **)filter);
904 } else if (g_strcmp0 (method_name, "ListItems") == 0) {
905 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
906 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
907 g_variant_builder_unref (list);
908 } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
909 g_dbus_method_invocation_return_value (invocation, NULL);
910 } else {
911 g_dbus_method_invocation_return_error (invocation,
912 G_DBUS_ERROR,
913 G_DBUS_ERROR_NOT_SUPPORTED,
914 "Method %s.%s not supported",
915 interface_name,
916 method_name);
917 }
918 }
919
920 static GVariant *
921 get_property_container_property (GDBusConnection *connection,
922 const char *sender,
923 const char *object_path,
924 const char *interface_name,
925 const char *property_name,
926 GError **error,
927 SourcePropertyRegistrationData *data)
928 {
929 if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
930 if (g_strcmp0 (property_name, "Parent") == 0) {
931 return g_variant_new_object_path (data->source_data->dbus_path);
932 } else if (g_strcmp0 (property_name, "Type") == 0) {
933 return g_variant_new_string ("container");
934 } else if (g_strcmp0 (property_name, "Path") == 0) {
935 return g_variant_new_string (object_path);
936 } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
937 return g_variant_new_string (data->display_name);
938 }
939 } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
940
941 if (g_strcmp0 (property_name, "ChildCount") == 0 ||
942 g_strcmp0 (property_name, "ContainerCount") == 0) {
943 /* don't include the 'all' row */
944 guint count;
945 count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (data->model), NULL) - 1;
946 return g_variant_new_uint32 (count);
947 } else if (g_strcmp0 (property_name, "ItemCount") == 0) {
948 return g_variant_new_uint32 (0);
949 } else if (g_strcmp0 (property_name, "Searchable") == 0) {
950 return g_variant_new_boolean (FALSE);
951 }
952 }
953 g_set_error (error,
954 G_DBUS_ERROR,
955 G_DBUS_ERROR_NOT_SUPPORTED,
956 "Property %s.%s not supported",
957 interface_name,
958 property_name);
959 return NULL;
960 }
961
962 static const GDBusInterfaceVTable property_vtable =
963 {
964 (GDBusInterfaceMethodCallFunc) property_container_method_call,
965 (GDBusInterfaceGetPropertyFunc) get_property_container_property,
966 NULL
967 };
968
969 static void
970 prop_model_row_inserted_cb (GtkTreeModel *model,
971 GtkTreePath *path,
972 GtkTreeIter *iter,
973 SourcePropertyRegistrationData *prop_data)
974 {
975 prop_data->updated = TRUE;
976 emit_updated_in_idle (prop_data->source_data->plugin);
977 }
978
979 static void
980 prop_model_row_changed_cb (GtkTreeModel *model,
981 GtkTreePath *path,
982 GtkTreeIter *iter,
983 SourcePropertyRegistrationData *prop_data)
984 {
985 char *value;
986 RBRefString *refstring;
987 gboolean is_all;
988 GList *l;
989
990 gtk_tree_model_get (model, iter,
991 RHYTHMDB_PROPERTY_MODEL_COLUMN_TITLE, &value,
992 RHYTHMDB_PROPERTY_MODEL_COLUMN_PRIORITY, &is_all,
993 -1);
994 if (is_all) {
995 g_free (value);
996 return;
997 }
998
999 refstring = rb_refstring_new (value);
1000 g_free (value);
1001
1002 for (l = prop_data->updated_values; l != NULL; l = l->next) {
1003 if (refstring == (RBRefString *)l->data) {
1004 rb_refstring_unref (refstring);
1005 return;
1006 }
1007 }
1008
1009 prop_data->updated_values = g_list_prepend (prop_data->updated_values, refstring);
1010 emit_updated_in_idle (prop_data->source_data->plugin);
1011 }
1012
1013 static void
1014 prop_model_row_deleted_cb (RhythmDBPropertyModel *prop_model,
1015 GtkTreePath *path,
1016 SourcePropertyRegistrationData *prop_data)
1017 {
1018 prop_data->updated = TRUE;
1019 emit_updated_in_idle (prop_data->source_data->plugin);
1020 }
1021
1022 static char **
1023 enumerate_property_value_subtree (GDBusConnection *connection,
1024 const char *sender,
1025 const char *object_path,
1026 SourcePropertyRegistrationData *data)
1027 {
1028 return (char **)g_new0(char *, 1);
1029 }
1030
1031 static GDBusInterfaceInfo **
1032 introspect_property_value_subtree (GDBusConnection *connection,
1033 const char *sender,
1034 const char *object_path,
1035 const char *node,
1036 SourcePropertyRegistrationData *data)
1037 {
1038 GPtrArray *p;
1039 GDBusInterfaceInfo *i;
1040
1041 p = g_ptr_array_new ();
1042
1043 i = g_dbus_node_info_lookup_interface (data->source_data->plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
1044 g_ptr_array_add (p, g_dbus_interface_info_ref (i));
1045
1046 i = g_dbus_node_info_lookup_interface (data->source_data->plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
1047 g_ptr_array_add (p, g_dbus_interface_info_ref (i));
1048
1049 g_ptr_array_add (p, NULL);
1050
1051 return (GDBusInterfaceInfo **)g_ptr_array_free (p, FALSE);
1052 }
1053
1054 static const GDBusInterfaceVTable *
1055 dispatch_property_value_subtree (GDBusConnection *connection,
1056 const char *sender,
1057 const char *object_path,
1058 const char *interface_name,
1059 const char *node,
1060 gpointer *out_user_data,
1061 SourcePropertyRegistrationData *data)
1062 {
1063 *out_user_data = data;
1064 return &property_value_vtable;
1065 }
1066
1067 static const GDBusSubtreeVTable property_subtree_vtable =
1068 {
1069 (GDBusSubtreeEnumerateFunc) enumerate_property_value_subtree,
1070 (GDBusSubtreeIntrospectFunc) introspect_property_value_subtree,
1071 (GDBusSubtreeDispatchFunc) dispatch_property_value_subtree
1072 };
1073
1074 static void
1075 register_property_container (GDBusConnection *connection,
1076 SourceRegistrationData *source_data,
1077 RhythmDBPropType property,
1078 const char *display_name)
1079 {
1080 SourcePropertyRegistrationData *data;
1081 GDBusInterfaceInfo *iface;
1082
1083 data = g_new0 (SourcePropertyRegistrationData, 1);
1084 data->source_data = source_data;
1085 data->property = property;
1086 data->display_name = g_strdup (display_name);
1087 data->dbus_path = g_strdup_printf ("%s/%s",
1088 source_data->dbus_path,
1089 rhythmdb_nice_elt_name_from_propid (source_data->plugin->db, property));
1090
1091 data->model = rhythmdb_property_model_new (source_data->plugin->db, property);
1092 g_object_set (data->model, "query-model", source_data->base_query_model, NULL);
1093 g_signal_connect (data->model, "row-inserted", G_CALLBACK (prop_model_row_inserted_cb), data);
1094 g_signal_connect (data->model, "row-changed", G_CALLBACK (prop_model_row_changed_cb), data);
1095 g_signal_connect (data->model, "row-deleted", G_CALLBACK (prop_model_row_deleted_cb), data);
1096
1097 data->dbus_subtree_id =
1098 g_dbus_connection_register_subtree (connection,
1099 data->dbus_path,
1100 &property_subtree_vtable,
1101 G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
1102 data,
1103 NULL,
1104 NULL);
1105
1106 iface = g_dbus_node_info_lookup_interface (source_data->plugin->node_info, MEDIA_SERVER2_OBJECT_IFACE_NAME);
1107 data->dbus_object_id[0] =
1108 g_dbus_connection_register_object (connection,
1109 data->dbus_path,
1110 iface,
1111 &property_vtable,
1112 data,
1113 NULL,
1114 NULL);
1115
1116 iface = g_dbus_node_info_lookup_interface (source_data->plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
1117 data->dbus_object_id[1] =
1118 g_dbus_connection_register_object (connection,
1119 data->dbus_path,
1120 iface,
1121 &property_vtable,
1122 data,
1123 NULL,
1124 NULL);
1125
1126 source_data->properties = g_list_append (source_data->properties, data);
1127 }
1128
1129
1130 /* source containers */
1131
1132 static void
1133 source_tracks_method_call (GDBusConnection *connection,
1134 const char *sender,
1135 const char *object_path,
1136 const char *interface_name,
1137 const char *method_name,
1138 GVariant *parameters,
1139 GDBusMethodInvocation *invocation,
1140 SourceRegistrationData *source_data)
1141 {
1142 GVariantBuilder *list;
1143
1144 if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) != 0) {
1145 rb_debug ("method call on unexpected interface %s", interface_name);
1146 return;
1147 }
1148
1149 if (g_strcmp0 (method_name, "ListChildren") == 0 ||
1150 g_strcmp0 (method_name, "ListItems") == 0) {
1151 GtkTreeModel *model;
1152 GtkTreeIter iter;
1153 guint list_offset;
1154 guint list_max;
1155 char **filter;
1156 guint count = 0;
1157
1158 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
1159 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
1160
1161 if (rb_str_in_strv ("*", (const char **)filter)) {
1162 g_strfreev (filter);
1163 filter = g_strdupv ((char **)all_entry_properties);
1164 }
1165
1166 model = GTK_TREE_MODEL (source_data->base_query_model);
1167 if (gtk_tree_model_get_iter_first (model, &iter)) {
1168 do {
1169 RhythmDBEntry *entry;
1170 GVariantBuilder *eb;
1171 int i;
1172 if (list_max > 0 && count == list_max) {
1173 break;
1174 }
1175
1176 entry = rhythmdb_query_model_iter_to_entry (source_data->base_query_model, &iter);
1177 if (entry == NULL) {
1178 continue;
1179 }
1180
1181 if (list_offset > 0) {
1182 list_offset--;
1183 continue;
1184 }
1185
1186 eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
1187 for (i = 0; filter[i] != NULL; i++) {
1188 GVariant *v;
1189 v = get_entry_property_value (entry, filter[i]);
1190 if (v != NULL) {
1191 g_variant_builder_add (eb, "{sv}", filter[i], v);
1192 }
1193 }
1194
1195 g_variant_builder_add (list, "a{sv}", eb);
1196 count++;
1197
1198 } while (gtk_tree_model_iter_next (model, &iter));
1199 }
1200 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
1201 g_variant_builder_unref (list);
1202
1203 g_strfreev (filter);
1204 } else if (g_strcmp0 (method_name, "ListContainers") == 0) {
1205 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
1206 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
1207 g_variant_builder_unref (list);
1208 } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
1209 g_dbus_method_invocation_return_value (invocation, NULL);
1210 } else {
1211 g_dbus_method_invocation_return_error (invocation,
1212 G_DBUS_ERROR,
1213 G_DBUS_ERROR_NOT_SUPPORTED,
1214 "Method %s.%s not supported",
1215 interface_name,
1216 method_name);
1217 }
1218 }
1219
1220 static GVariant *
1221 get_source_tracks_property (GDBusConnection *connection,
1222 const char *sender,
1223 const char *object_path,
1224 const char *interface_name,
1225 const char *property_name,
1226 GError **error,
1227 SourceRegistrationData *source_data)
1228 {
1229 GVariant *v;
1230 char *name;
1231
1232 if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
1233 if (g_strcmp0 (property_name, "Parent") == 0) {
1234 if (source_data->flat) {
1235 return g_variant_new_object_path (source_data->parent_dbus_path);
1236 } else {
1237 return g_variant_new_object_path (source_data->dbus_path);
1238 }
1239 } else if (g_strcmp0 (property_name, "Type") == 0) {
1240 return g_variant_new_string ("container");
1241 } else if (g_strcmp0 (property_name, "Path") == 0) {
1242 return g_variant_new_string (object_path);
1243 } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
1244 if (source_data->flat) {
1245 g_object_get (source_data->source, "name", &name, NULL);
1246 v = g_variant_new_string (name);
1247 g_free (name);
1248 return v;
1249 } else {
1250 return g_variant_new_string (_("All Tracks"));
1251 }
1252 }
1253 } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
1254
1255 if (g_strcmp0 (property_name, "ChildCount") == 0 ||
1256 g_strcmp0 (property_name, "ItemCount") == 0) {
1257 int count;
1258 count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source_data->base_query_model), NULL);
1259 return g_variant_new_uint32 (count);
1260 } else if (g_strcmp0 (property_name, "ContainerCount") == 0) {
1261 return g_variant_new_uint32 (0);
1262 } else if (g_strcmp0 (property_name, "Searchable") == 0) {
1263 return g_variant_new_boolean (FALSE);
1264 }
1265 }
1266 g_set_error (error,
1267 G_DBUS_ERROR,
1268 G_DBUS_ERROR_NOT_SUPPORTED,
1269 "Property %s.%s not supported",
1270 interface_name,
1271 property_name);
1272 return NULL;
1273 }
1274
1275 static void
1276 add_source_tracks_property (RBMediaServer2Plugin *plugin, GVariantBuilder *properties, const char *iface, const char *property, SourceRegistrationData *source_data)
1277 {
1278 GVariant *v;
1279 v = get_source_tracks_property (plugin->connection, NULL, source_data->dbus_path, iface, property, NULL, source_data);
1280 g_variant_builder_add (properties, "{sv}", property, v);
1281 }
1282
1283 static void
1284 emit_source_tracks_property_updates (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data)
1285 {
1286 GError *error = NULL;
1287 const char *invalidated[] = { NULL };
1288 GVariantBuilder *properties;
1289 GVariant *parameters;
1290 char *path;
1291
1292 rb_debug ("updating properties for source %s", source_data->dbus_path);
1293 properties = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
1294 add_source_tracks_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ItemCount", source_data);
1295 add_source_tracks_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ChildCount", source_data);
1296 add_source_tracks_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ContainerCount", source_data);
1297
1298 parameters = g_variant_new ("(sa{sv}^as)",
1299 MEDIA_SERVER2_CONTAINER_IFACE_NAME,
1300 properties,
1301 invalidated);
1302 g_variant_builder_unref (properties);
1303 if (source_data->flat) {
1304 path = g_strdup (source_data->dbus_path);
1305 } else {
1306 path = g_strdup_printf ("%s/all", source_data->dbus_path);
1307 }
1308 g_dbus_connection_emit_signal (plugin->connection,
1309 NULL,
1310 path,
1311 "org.freedesktop.DBus.Properties",
1312 "PropertiesChanged",
1313 parameters,
1314 &error);
1315 g_free (path);
1316 if (error != NULL) {
1317 g_warning ("Unable to send property changes for MediaServer2 container %s: %s",
1318 source_data->dbus_path,
1319 error->message);
1320 g_clear_error (&error);
1321 }
1322 }
1323
1324 static const GDBusInterfaceVTable source_tracks_vtable =
1325 {
1326 (GDBusInterfaceMethodCallFunc) source_tracks_method_call,
1327 (GDBusInterfaceGetPropertyFunc) get_source_tracks_property,
1328 NULL
1329 };
1330
1331
1332 static void
1333 source_properties_method_call (GDBusConnection *connection,
1334 const char *sender,
1335 const char *object_path,
1336 const char *interface_name,
1337 const char *method_name,
1338 GVariant *parameters,
1339 GDBusMethodInvocation *invocation,
1340 SourceRegistrationData *source_data)
1341 {
1342 GVariantBuilder *list;
1343 guint value_count;
1344
1345 if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) != 0) {
1346 rb_debug ("method call on unexpected interface %s", interface_name);
1347 return;
1348 }
1349
1350 if (g_strcmp0 (method_name, "ListChildren") == 0 ||
1351 g_strcmp0 (method_name, "ListContainers") == 0) {
1352 GList *l;
1353 guint list_offset;
1354 guint list_max;
1355 const char **filter;
1356 guint count = 0;
1357 gboolean all_props;
1358 GVariantBuilder *eb;
1359
1360 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
1361 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
1362
1363 all_props = rb_str_in_strv ("*", filter);
1364
1365 /* 'all tracks' container */
1366 if (list_offset == 0) {
1367 eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
1368 value_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source_data->base_query_model), NULL);
1369 if (all_props || rb_str_in_strv ("Parent", filter)) {
1370 g_variant_builder_add (eb, "{sv}", "Parent", g_variant_new_object_path (object_path));
1371 }
1372 if (all_props || rb_str_in_strv ("Type", filter)) {
1373 g_variant_builder_add (eb, "{sv}", "Type", g_variant_new_string ("container"));
1374 }
1375 if (all_props || rb_str_in_strv ("Path", filter)) {
1376 char *path;
1377 path = g_strdup_printf ("%s/all", object_path);
1378 g_variant_builder_add (eb, "{sv}", "Path", g_variant_new_string (path));
1379 g_free (path);
1380 }
1381 if (all_props || rb_str_in_strv ("DisplayName", filter)) {
1382 g_variant_builder_add (eb, "{sv}", "DisplayName", g_variant_new_string (_("All Tracks")));
1383 }
1384 if (all_props || rb_str_in_strv ("ChildCount", filter)) {
1385 g_variant_builder_add (eb, "{sv}", "ChildCount", g_variant_new_uint32 (value_count));
1386 }
1387 if (all_props || rb_str_in_strv ("ItemCount", filter)) {
1388 g_variant_builder_add (eb, "{sv}", "ItemCount", g_variant_new_uint32 (value_count));
1389 }
1390 if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
1391 g_variant_builder_add (eb, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
1392 }
1393 if (all_props || rb_str_in_strv ("Searchable", filter)) {
1394 g_variant_builder_add (eb, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
1395 }
1396
1397 g_variant_builder_add (list, "a{sv}", eb);
1398 count++;
1399 } else {
1400 list_offset--;
1401 }
1402
1403 /* property-based containers */
1404 for (l = source_data->properties; l != NULL; l = l->next) {
1405 SourcePropertyRegistrationData *prop_data = l->data;
1406 if (list_max > 0 && count == list_max) {
1407 break;
1408 }
1409
1410 if (list_offset > 0) {
1411 list_offset--;
1412 continue;
1413 }
1414
1415 value_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (prop_data->model), NULL) - 1;
1416
1417 eb = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
1418 if (all_props || rb_str_in_strv ("Parent", filter)) {
1419 g_variant_builder_add (eb, "{sv}", "Parent", g_variant_new_object_path (object_path));
1420 }
1421 if (all_props || rb_str_in_strv ("Type", filter)) {
1422 g_variant_builder_add (eb, "{sv}", "Type", g_variant_new_string ("container"));
1423 }
1424 if (all_props || rb_str_in_strv ("Path", filter)) {
1425 g_variant_builder_add (eb, "{sv}", "Path", g_variant_new_string (prop_data->dbus_path));
1426 }
1427 if (all_props || rb_str_in_strv ("DisplayName", filter)) {
1428 g_variant_builder_add (eb, "{sv}", "DisplayName", g_variant_new_string (prop_data->display_name));
1429 }
1430 if (all_props || rb_str_in_strv ("ChildCount", filter)) {
1431 g_variant_builder_add (eb, "{sv}", "ChildCount", g_variant_new_uint32 (value_count));
1432 }
1433 if (all_props || rb_str_in_strv ("ItemCount", filter)) {
1434 g_variant_builder_add (eb, "{sv}", "ItemCount", g_variant_new_uint32 (0));
1435 }
1436 if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
1437 g_variant_builder_add (eb, "{sv}", "ContainerCount", g_variant_new_uint32 (value_count));
1438 }
1439 if (all_props || rb_str_in_strv ("Searchable", filter)) {
1440 g_variant_builder_add (eb, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
1441 }
1442
1443 g_variant_builder_add (list, "a{sv}", eb);
1444 count++;
1445 }
1446
1447 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
1448 g_variant_builder_unref (list);
1449
1450 g_strfreev ((char **)filter);
1451 } else if (g_strcmp0 (method_name, "ListItems") == 0) {
1452 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
1453 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
1454 g_variant_builder_unref (list);
1455 } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
1456 g_dbus_method_invocation_return_value (invocation, NULL);
1457 } else {
1458 g_dbus_method_invocation_return_error (invocation,
1459 G_DBUS_ERROR,
1460 G_DBUS_ERROR_NOT_SUPPORTED,
1461 "Method %s.%s not supported",
1462 interface_name,
1463 method_name);
1464 }
1465 }
1466
1467 static GVariant *
1468 get_source_properties_property (GDBusConnection *connection,
1469 const char *sender,
1470 const char *object_path,
1471 const char *interface_name,
1472 const char *property_name,
1473 GError **error,
1474 SourceRegistrationData *source_data)
1475 {
1476 GVariant *v;
1477 char *name;
1478
1479 if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
1480 if (g_strcmp0 (property_name, "Parent") == 0) {
1481 return g_variant_new_object_path (source_data->parent_dbus_path);
1482 } else if (g_strcmp0 (property_name, "Type") == 0) {
1483 return g_variant_new_string ("container");
1484 } else if (g_strcmp0 (property_name, "Path") == 0) {
1485 return g_variant_new_string (object_path);
1486 } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
1487 g_object_get (source_data->source, "name", &name, NULL);
1488 v = g_variant_new_string (name);
1489 g_free (name);
1490 return v;
1491 }
1492 } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
1493
1494 if (g_strcmp0 (property_name, "ChildCount") == 0 ||
1495 g_strcmp0 (property_name, "ContainerCount") == 0) {
1496 return g_variant_new_uint32 (g_list_length (source_data->properties) + 1);
1497 } else if (g_strcmp0 (property_name, "ItemCount") == 0) {
1498 return g_variant_new_uint32 (0);
1499 } else if (g_strcmp0 (property_name, "Searchable") == 0) {
1500 return g_variant_new_boolean (FALSE);
1501 }
1502 }
1503 g_set_error (error,
1504 G_DBUS_ERROR,
1505 G_DBUS_ERROR_NOT_SUPPORTED,
1506 "Property %s.%s not supported",
1507 interface_name,
1508 property_name);
1509 return NULL;
1510 }
1511
1512 static const GDBusInterfaceVTable source_properties_vtable =
1513 {
1514 (GDBusInterfaceMethodCallFunc) source_properties_method_call,
1515 (GDBusInterfaceGetPropertyFunc) get_source_properties_property,
1516 NULL
1517 };
1518
1519 /* source container registration */
1520
1521 static void
1522 add_source_container (GVariantBuilder *list, SourceRegistrationData *source_data, const char **filter)
1523 {
1524 GVariantBuilder *i;
1525 gboolean all_props;
1526
1527 i = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
1528 all_props = rb_str_in_strv ("*", filter);
1529
1530
1531 if (all_props || rb_str_in_strv ("Parent", filter)) {
1532 g_variant_builder_add (i, "{sv}", "Parent", g_variant_new_object_path (source_data->parent_dbus_path));
1533 }
1534 if (all_props || rb_str_in_strv ("Type", filter)) {
1535 g_variant_builder_add (i, "{sv}", "Type", g_variant_new_string ("container"));
1536 }
1537 if (all_props || rb_str_in_strv ("Path", filter)) {
1538 g_variant_builder_add (i, "{sv}", "Path", g_variant_new_string (source_data->dbus_path));
1539 }
1540 if (all_props || rb_str_in_strv ("DisplayName", filter)) {
1541 char *name;
1542 g_object_get (source_data->source, "name", &name, NULL);
1543 g_variant_builder_add (i, "{sv}", "DisplayName", g_variant_new_string (name));
1544 g_free (name);
1545 }
1546 if (source_data->flat) {
1547 int entry_count;
1548 entry_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source_data->base_query_model), NULL);
1549 if (all_props || rb_str_in_strv ("ChildCount", filter)) {
1550 g_variant_builder_add (i, "{sv}", "ChildCount", g_variant_new_uint32 (entry_count));
1551 }
1552 if (all_props || rb_str_in_strv ("ItemCount", filter)) {
1553 g_variant_builder_add (i, "{sv}", "ItemCount", g_variant_new_uint32 (entry_count));
1554 }
1555 if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
1556 g_variant_builder_add (i, "{sv}", "ContainerCount", g_variant_new_uint32 (0));
1557 }
1558 } else {
1559 int child_count = g_list_length (source_data->properties) + 1;
1560 if (all_props || rb_str_in_strv ("ChildCount", filter)) {
1561 g_variant_builder_add (i, "{sv}", "ChildCount", g_variant_new_uint32 (child_count));
1562 }
1563 if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
1564 g_variant_builder_add (i, "{sv}", "ContainerCount", g_variant_new_uint32 (child_count));
1565 }
1566 if (all_props || rb_str_in_strv ("ItemCount", filter)) {
1567 g_variant_builder_add (i, "{sv}", "ItemCount", g_variant_new_uint32 (0));
1568 }
1569 }
1570 if (all_props || rb_str_in_strv ("Searchable", filter)) {
1571 g_variant_builder_add (i, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
1572 }
1573
1574 g_variant_builder_add (list, "a{sv}", i);
1575 }
1576
1577 static int
1578 count_sources_by_parent (RBMediaServer2Plugin *plugin, const char *parent_dbus_path)
1579 {
1580 GList *l;
1581 int count = 0;
1582 for (l = plugin->sources; l != NULL; l = l->next) {
1583 SourceRegistrationData *source_data;
1584 source_data = l->data;
1585 if (g_strcmp0 (source_data->parent_dbus_path, parent_dbus_path) == 0) {
1586 count++;
1587 }
1588 }
1589 return count;
1590 }
1591
1592 static void
1593 list_sources_by_parent (RBMediaServer2Plugin *plugin,
1594 GVariantBuilder *list,
1595 const char *parent_dbus_path,
1596 guint *list_offset,
1597 guint *list_count,
1598 guint list_max,
1599 const char **filter)
1600 {
1601 GList *l;
1602 for (l = plugin->sources; l != NULL; l = l->next) {
1603 SourceRegistrationData *source_data;
1604 if (list_max > 0 && (*list_count) == list_max) {
1605 break;
1606 }
1607
1608 source_data = l->data;
1609 if (g_strcmp0 (source_data->parent_dbus_path, parent_dbus_path) != 0) {
1610 continue;
1611 }
1612
1613 if ((*list_offset) > 0) {
1614 (*list_offset)--;
1615 continue;
1616 }
1617
1618 add_source_container (list, source_data, filter);
1619 (*list_count)++;
1620 }
1621 }
1622
1623 static SourceRegistrationData *
1624 find_registration_data (RBMediaServer2Plugin *plugin, RBSource *source)
1625 {
1626 GList *l;
1627 for (l = plugin->sources; l != NULL; l = l->next) {
1628 SourceRegistrationData *data = l->data;
1629 if (data->source == source) {
1630 return data;
1631 }
1632 }
1633 return NULL;
1634 }
1635
1636 static void
1637 destroy_registration_data (SourceRegistrationData *source_data)
1638 {
1639 g_free (source_data->dbus_path);
1640 g_free (source_data->parent_dbus_path);
1641 g_object_unref (source_data->source);
1642 g_object_unref (source_data->base_query_model);
1643
1644 g_free (source_data);
1645 }
1646
1647 static void
1648 source_parent_updated (SourceRegistrationData *source_data)
1649 {
1650 GList *l;
1651 for (l = source_data->plugin->categories; l != NULL; l = l->next) {
1652 CategoryRegistrationData *category_data = l->data;
1653 if (g_strcmp0 (source_data->parent_dbus_path, category_data->dbus_path) == 0) {
1654 category_data->updated = TRUE;
1655 break;
1656 }
1657 }
1658 if (l == NULL) {
1659 source_data->plugin->root_updated = TRUE;
1660 }
1661
1662 emit_updated_in_idle (source_data->plugin);
1663 }
1664
1665 static void
1666 source_updated (SourceRegistrationData *source_data)
1667 {
1668 source_data->updated = TRUE;
1669 emit_updated_in_idle (source_data->plugin);
1670 }
1671
1672 /* signal handlers for source container updates */
1673
1674 static void
1675 row_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, SourceRegistrationData *source_data)
1676 {
1677 source_updated (source_data);
1678 }
1679
1680 static void
1681 row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, SourceRegistrationData *source_data)
1682 {
1683 source_updated (source_data);
1684 }
1685
1686 static void
1687 entry_prop_changed_cb (RhythmDBQueryModel *model,
1688 RhythmDBEntry *entry,
1689 RhythmDBPropType prop,
1690 const GValue *old,
1691 const GValue *new_value,
1692 SourceRegistrationData *source_data)
1693 {
1694 GList *l;
1695
1696 if (entry_property_maps (prop) == FALSE) {
1697 return;
1698 }
1699
1700 source_updated (source_data);
1701 for (l = source_data->properties; l != NULL; l = l->next) {
1702 SourcePropertyRegistrationData *prop_data = l->data;
1703 RBRefString *value;
1704
1705 /* property model signal handlers will take care of this */
1706 if (prop == prop_data->property)
1707 continue;
1708
1709 prop_data->updated = TRUE;
1710 value = rhythmdb_entry_get_refstring (entry, prop_data->property);
1711 if (g_list_find (prop_data->updated_values, value) == NULL) {
1712 prop_data->updated_values =
1713 g_list_prepend (prop_data->updated_values, value);
1714 }
1715 }
1716 }
1717
1718 static void
1719 disconnect_query_model_signals (SourceRegistrationData *source_data)
1720 {
1721 g_signal_handlers_disconnect_by_func (source_data->base_query_model, row_inserted_cb, source_data);
1722 g_signal_handlers_disconnect_by_func (source_data->base_query_model, entry_prop_changed_cb, source_data);
1723 g_signal_handlers_disconnect_by_func (source_data->base_query_model, row_deleted_cb, source_data);
1724 }
1725
1726 static void
1727 connect_query_model_signals (SourceRegistrationData *source_data)
1728 {
1729 g_signal_connect (source_data->base_query_model, "row-inserted", G_CALLBACK (row_inserted_cb), source_data);
1730 g_signal_connect (source_data->base_query_model, "entry-prop-changed", G_CALLBACK (entry_prop_changed_cb), source_data);
1731 g_signal_connect (source_data->base_query_model, "row-deleted", G_CALLBACK (row_deleted_cb), source_data);
1732 }
1733
1734 static void
1735 base_query_model_updated_cb (RBSource *source, GParamSpec *pspec, SourceRegistrationData *source_data)
1736 {
1737 GList *l;
1738
1739 if (source_data->base_query_model != NULL) {
1740 disconnect_query_model_signals (source_data);
1741 g_object_unref (source_data->base_query_model);
1742 }
1743
1744 g_object_get (source, "base-query-model", &source_data->base_query_model, NULL);
1745 connect_query_model_signals (source_data);
1746
1747 for (l = source_data->properties; l != NULL; l = l->next) {
1748 SourcePropertyRegistrationData *prop_data = l->data;
1749 g_object_set (prop_data->model, "query-model", source_data->base_query_model, NULL);
1750 }
1751
1752 source_updated (source_data);
1753 }
1754
1755 static void
1756 name_updated_cb (RBSource *source, GParamSpec *pspec, SourceRegistrationData *source_data)
1757 {
1758 source_updated (source_data);
1759 }
1760
1761 static void
1762 source_deleted_cb (RBDisplayPage *page, RBMediaServer2Plugin *plugin)
1763 {
1764 SourceRegistrationData *source_data;
1765
1766 source_data = find_registration_data (plugin, RB_SOURCE (page));
1767 if (source_data != NULL) {
1768 rb_debug ("source for container %s deleted", source_data->dbus_path);
1769 unregister_source_container (plugin, source_data, FALSE);
1770 }
1771 }
1772
1773
1774 static SourceRegistrationData *
1775 register_source_container (RBMediaServer2Plugin *plugin,
1776 RBSource *source,
1777 const char *dbus_path,
1778 const char *parent_dbus_path,
1779 gboolean flat)
1780 {
1781 SourceRegistrationData *source_data;
1782 GDBusInterfaceInfo *container_iface;
1783
1784 source_data = g_new0 (SourceRegistrationData, 1);
1785 source_data->source = g_object_ref (source);
1786 source_data->dbus_path = g_strdup (dbus_path);
1787 source_data->parent_dbus_path = g_strdup (parent_dbus_path);
1788 source_data->plugin = plugin;
1789 source_data->flat = flat;
1790
1791 container_iface = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
1792 if (flat) {
1793 register_object (plugin, &source_tracks_vtable, container_iface, dbus_path, source_data, source_data->dbus_reg_id);
1794 } else {
1795 char *tracks_path;
1796
1797 register_object (plugin, &source_properties_vtable, container_iface, dbus_path, source_data, source_data->dbus_reg_id);
1798
1799 tracks_path = g_strdup_printf ("%s/all", dbus_path);
1800 register_object (plugin, &source_tracks_vtable, container_iface, tracks_path, source_data, source_data->all_tracks_reg_id);
1801 }
1802
1803 g_object_get (source, "base-query-model", &source_data->base_query_model, NULL);
1804 connect_query_model_signals (source_data);
1805 g_signal_connect (source, "notify::base-query-model", G_CALLBACK (base_query_model_updated_cb), source_data);
1806 g_signal_connect (source, "notify::name", G_CALLBACK (name_updated_cb), source_data);
1807 g_signal_connect (source, "deleted", G_CALLBACK (source_deleted_cb), plugin);
1808
1809 /* add to registration list */
1810 plugin->sources = g_list_append (plugin->sources, source_data);
1811
1812 /* emit 'updated' signal on parent container */
1813 g_dbus_connection_emit_signal (plugin->connection, NULL, parent_dbus_path, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "Updated", NULL, NULL);
1814
1815 return source_data;
1816 }
1817
1818 static void
1819 unregister_source_container (RBMediaServer2Plugin *plugin, SourceRegistrationData *source_data, gboolean deactivating)
1820 {
1821 /* if object registration ids exist, unregister the object */
1822 unregister_object (plugin, source_data->dbus_reg_id);
1823
1824 /* remove signal handlers */
1825 disconnect_query_model_signals (source_data);
1826 g_signal_handlers_disconnect_by_func (source_data->source, G_CALLBACK (base_query_model_updated_cb), source_data);
1827 g_signal_handlers_disconnect_by_func (source_data->source, G_CALLBACK (name_updated_cb), source_data);
1828
1829 if (deactivating == FALSE) {
1830 /* remove from registration list */
1831 plugin->sources = g_list_remove (plugin->sources, source_data);
1832
1833 /* emit 'updated' signal on parent container */
1834 source_parent_updated (source_data);
1835 destroy_registration_data (source_data);
1836 }
1837 }
1838
1839 /* source category containers */
1840
1841 static void
1842 add_category_container (GVariantBuilder *list, CategoryRegistrationData *data, const char **filter)
1843 {
1844 GVariantBuilder *i;
1845 int source_count;
1846 gboolean all_props;
1847
1848 i = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
1849 all_props = rb_str_in_strv ("*", filter);
1850
1851 source_count = count_sources_by_parent (data->plugin, data->dbus_path);
1852
1853 if (all_props || rb_str_in_strv ("Parent", filter)) {
1854 g_variant_builder_add (i, "{sv}", "Parent", g_variant_new_object_path (data->parent_dbus_path));
1855 }
1856 if (all_props || rb_str_in_strv ("Type", filter)) {
1857 g_variant_builder_add (i, "{sv}", "Type", g_variant_new_string ("container"));
1858 }
1859 if (all_props || rb_str_in_strv ("Path", filter)) {
1860 g_variant_builder_add (i, "{sv}", "Path", g_variant_new_string (data->dbus_path));
1861 }
1862 if (all_props || rb_str_in_strv ("DisplayName", filter)) {
1863 g_variant_builder_add (i, "{sv}", "DisplayName", g_variant_new_string (data->name));
1864 }
1865 if (all_props || rb_str_in_strv ("ChildCount", filter)) {
1866 g_variant_builder_add (i, "{sv}", "ChildCount", g_variant_new_uint32 (source_count));
1867 }
1868 if (all_props || rb_str_in_strv ("ItemCount", filter)) {
1869 g_variant_builder_add (i, "{sv}", "ItemCount", g_variant_new_uint32 (0));
1870 }
1871 if (all_props || rb_str_in_strv ("ContainerCount", filter)) {
1872 g_variant_builder_add (i, "{sv}", "ContainerCount", g_variant_new_uint32 (source_count));
1873 }
1874 if (all_props || rb_str_in_strv ("Searchable", filter)) {
1875 g_variant_builder_add (i, "{sv}", "Searchable", g_variant_new_boolean (FALSE));
1876 }
1877
1878 g_variant_builder_add (list, "a{sv}", i);
1879 }
1880
1881 static void
1882 list_categories_by_parent (RBMediaServer2Plugin *plugin,
1883 GVariantBuilder *list,
1884 const char *parent_dbus_path,
1885 guint *list_offset,
1886 guint *list_count,
1887 guint list_max,
1888 const char **filter)
1889 {
1890 GList *l;
1891 for (l = plugin->categories; l != NULL; l = l->next) {
1892 CategoryRegistrationData *category_data;
1893 if (list_max > 0 && (*list_count) == list_max) {
1894 break;
1895 }
1896
1897 category_data = l->data;
1898 if (g_strcmp0 (category_data->parent_dbus_path, parent_dbus_path) != 0) {
1899 continue;
1900 }
1901
1902 if ((*list_offset) > 0) {
1903 (*list_offset)--;
1904 continue;
1905 }
1906
1907 add_category_container (list, category_data, filter);
1908 (*list_count)++;
1909 }
1910 }
1911
1912 static int
1913 count_categories_by_parent (RBMediaServer2Plugin *plugin, const char *parent_dbus_path)
1914 {
1915 GList *l;
1916 int count = 0;
1917 for (l = plugin->categories; l != NULL; l = l->next) {
1918 CategoryRegistrationData *category_data;
1919 category_data = l->data;
1920 if (g_strcmp0 (category_data->parent_dbus_path, parent_dbus_path) == 0) {
1921 count++;
1922 }
1923 }
1924 return count;
1925 }
1926
1927
1928 static void
1929 category_container_method_call (GDBusConnection *connection,
1930 const char *sender,
1931 const char *object_path,
1932 const char *interface_name,
1933 const char *method_name,
1934 GVariant *parameters,
1935 GDBusMethodInvocation *invocation,
1936 CategoryRegistrationData *data)
1937 {
1938 if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
1939 guint list_offset;
1940 guint list_max;
1941 guint list_count = 0;
1942 const char **filter;
1943 GVariantBuilder *list;
1944
1945 if (g_strcmp0 (method_name, "ListChildren") == 0 ||
1946 g_strcmp0 (method_name, "ListContainers") == 0) {
1947 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
1948 rb_debug ("listing containers (%s) - offset %d, max %d", method_name, list_offset, list_max);
1949
1950 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
1951 list_sources_by_parent (data->plugin, list, object_path, &list_offset, &list_count, list_max, filter);
1952 rb_debug ("returned %d containers", list_count);
1953
1954 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
1955 g_variant_builder_unref (list);
1956 g_strfreev ((char **)filter);
1957 } else if (g_strcmp0 (method_name, "ListItems") == 0) {
1958 rb_debug ("listing items");
1959 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
1960 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
1961 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
1962 g_variant_builder_unref (list);
1963 g_strfreev ((char **)filter);
1964 } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
1965 rb_debug ("search not supported");
1966 g_dbus_method_invocation_return_value (invocation, NULL);
1967 }
1968 } else {
1969 g_dbus_method_invocation_return_error (invocation,
1970 G_DBUS_ERROR,
1971 G_DBUS_ERROR_NOT_SUPPORTED,
1972 "Method %s.%s not supported",
1973 interface_name,
1974 method_name);
1975 }
1976 }
1977
1978 static GVariant *
1979 get_category_container_property (GDBusConnection *connection,
1980 const char *sender,
1981 const char *object_path,
1982 const char *interface_name,
1983 const char *property_name,
1984 GError **error,
1985 CategoryRegistrationData *data)
1986 {
1987 int count;
1988 if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
1989 if (g_strcmp0 (property_name, "Parent") == 0) {
1990 return g_variant_new_object_path (data->parent_dbus_path);
1991 } else if (g_strcmp0 (property_name, "Type") == 0) {
1992 return g_variant_new_string ("container");
1993 } else if (g_strcmp0 (property_name, "Path") == 0) {
1994 return g_variant_new_string (object_path);
1995 } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
1996 return g_variant_new_string (data->name);
1997 }
1998 } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
1999 if (g_strcmp0 (property_name, "ChildCount") == 0 ||
2000 g_strcmp0 (property_name, "ContainerCount") == 0) {
2001 count = count_sources_by_parent (data->plugin, object_path);
2002 rb_debug ("child/container count %d", count);
2003 return g_variant_new_uint32 (count);
2004 } else if (g_strcmp0 (property_name, "ItemCount") == 0) {
2005 return g_variant_new_uint32 (0);
2006 } else if (g_strcmp0 (property_name, "Searchable") == 0) {
2007 return g_variant_new_boolean (FALSE);
2008 }
2009 }
2010 g_set_error (error,
2011 G_DBUS_ERROR,
2012 G_DBUS_ERROR_NOT_SUPPORTED,
2013 "Property %s.%s not supported",
2014 interface_name,
2015 property_name);
2016 return NULL;
2017 }
2018
2019 static void
2020 add_category_container_property (RBMediaServer2Plugin *plugin, GVariantBuilder *properties, const char *iface, const char *property, CategoryRegistrationData *category_data)
2021 {
2022 GVariant *v;
2023 v = get_category_container_property (plugin->connection, NULL, category_data->dbus_path, iface, property, NULL, category_data);
2024 g_variant_builder_add (properties, "{sv}", property, v);
2025 }
2026
2027 static void
2028 emit_category_container_property_updates (RBMediaServer2Plugin *plugin, CategoryRegistrationData *category_data)
2029 {
2030 GError *error = NULL;
2031 const char *invalidated[] = { NULL };
2032 GVariantBuilder *properties;
2033 GVariant *parameters;
2034
2035 rb_debug ("updating properties for category %s", category_data->dbus_path);
2036 properties = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
2037 add_category_container_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ItemCount", category_data);
2038 add_category_container_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ChildCount", category_data);
2039 add_category_container_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ContainerCount", category_data);
2040
2041 parameters = g_variant_new ("(sa{sv}^as)",
2042 MEDIA_SERVER2_CONTAINER_IFACE_NAME,
2043 properties,
2044 invalidated);
2045 g_variant_builder_unref (properties);
2046 g_dbus_connection_emit_signal (plugin->connection,
2047 NULL,
2048 category_data->dbus_path,
2049 "org.freedesktop.DBus.Properties",
2050 "PropertiesChanged",
2051 parameters,
2052 &error);
2053 if (error != NULL) {
2054 g_warning ("Unable to send property changes for MediaServer2 container %s: %s",
2055 category_data->dbus_path,
2056 error->message);
2057 g_clear_error (&error);
2058 }
2059 }
2060
2061
2062 static const GDBusInterfaceVTable category_container_vtable =
2063 {
2064 (GDBusInterfaceMethodCallFunc) category_container_method_call,
2065 (GDBusInterfaceGetPropertyFunc) get_category_container_property,
2066 NULL
2067 };
2068
2069 static void
2070 register_category_container (RBMediaServer2Plugin *plugin,
2071 const char *name,
2072 const char *dbus_path,
2073 const char *parent_dbus_path,
2074 gboolean (*match_source) (RBSource *source))
2075 {
2076 CategoryRegistrationData *category_data;
2077 GDBusInterfaceInfo *container_iface;
2078
2079 category_data = g_new0 (CategoryRegistrationData, 1);
2080 category_data->name = g_strdup (name);
2081 category_data->dbus_path = g_strdup (dbus_path);
2082 category_data->parent_dbus_path = g_strdup (parent_dbus_path);
2083 category_data->plugin = plugin;
2084 category_data->match_source = match_source;
2085
2086 container_iface = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
2087 register_object (plugin, &category_container_vtable, container_iface, dbus_path, category_data, category_data->dbus_reg_id);
2088
2089 /* add to registration list */
2090 plugin->categories = g_list_append (plugin->categories, category_data);
2091
2092 /* emit 'updated' signal on parent container */
2093 g_dbus_connection_emit_signal (plugin->connection, NULL, parent_dbus_path, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "Updated", NULL, NULL);
2094 }
2095
2096 static void
2097 destroy_category_data (CategoryRegistrationData *category_data)
2098 {
2099 g_free (category_data->name);
2100 g_free (category_data->dbus_path);
2101 g_free (category_data->parent_dbus_path);
2102 g_free (category_data);
2103 }
2104
2105 static void
2106 unregister_category_container (RBMediaServer2Plugin *plugin, CategoryRegistrationData *category_data, gboolean deactivating)
2107 {
2108 /* if object registration ids exist, unregister the object */
2109 unregister_object (plugin, category_data->dbus_reg_id);
2110
2111 if (deactivating == FALSE) {
2112 /* remove from registration list */
2113 plugin->categories = g_list_remove (plugin->categories, category_data);
2114
2115 /* emit 'updated' signal on parent container */
2116 g_dbus_connection_emit_signal (plugin->connection, NULL, category_data->parent_dbus_path, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "Updated", NULL, NULL);
2117
2118 destroy_category_data (category_data);
2119 }
2120 }
2121
2122 /* root container */
2123
2124 static void
2125 root_method_call (GDBusConnection *connection,
2126 const char *sender,
2127 const char *object_path,
2128 const char *interface_name,
2129 const char *method_name,
2130 GVariant *parameters,
2131 GDBusMethodInvocation *invocation,
2132 RBMediaServer2Plugin *plugin)
2133 {
2134 if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
2135 guint list_offset;
2136 guint list_max;
2137 guint list_count = 0;
2138 const char **filter;
2139 GVariantBuilder *list;
2140
2141 if (g_strcmp0 (method_name, "ListChildren") == 0 ||
2142 g_strcmp0 (method_name, "ListContainers") == 0) {
2143 rb_debug ("listing containers (%s)", method_name);
2144 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
2145
2146 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
2147 list_sources_by_parent (plugin, list, object_path, &list_offset, &list_count, list_max, filter);
2148 list_categories_by_parent (plugin, list, object_path, &list_offset, &list_count, list_max, filter);
2149
2150 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
2151 g_variant_builder_unref (list);
2152 g_strfreev ((char **)filter);
2153 } else if (g_strcmp0 (method_name, "ListItems") == 0) {
2154 rb_debug ("listing items");
2155 g_variant_get (parameters, "(uu^as)", &list_offset, &list_max, &filter);
2156 list = g_variant_builder_new (G_VARIANT_TYPE ("aa{sv}"));
2157 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(aa{sv})", list));
2158 g_variant_builder_unref (list);
2159 g_strfreev ((char **)filter);
2160 } else if (g_strcmp0 (method_name, "SearchObjects") == 0) {
2161 rb_debug ("search not supported");
2162 g_dbus_method_invocation_return_value (invocation, NULL);
2163 }
2164 } else {
2165 g_dbus_method_invocation_return_error (invocation,
2166 G_DBUS_ERROR,
2167 G_DBUS_ERROR_NOT_SUPPORTED,
2168 "Method %s.%s not supported",
2169 interface_name,
2170 method_name);
2171 }
2172 }
2173
2174 static GVariant *
2175 get_root_property (GDBusConnection *connection,
2176 const char *sender,
2177 const char *object_path,
2178 const char *interface_name,
2179 const char *property_name,
2180 GError **error,
2181 RBMediaServer2Plugin *plugin)
2182 {
2183 GVariant *v;
2184 int count;
2185 if (g_strcmp0 (interface_name, MEDIA_SERVER2_OBJECT_IFACE_NAME) == 0) {
2186 if (g_strcmp0 (property_name, "Parent") == 0) {
2187 return g_variant_new_object_path (object_path);
2188 } else if (g_strcmp0 (property_name, "Type") == 0) {
2189 return g_variant_new_string ("container");
2190 } else if (g_strcmp0 (property_name, "Path") == 0) {
2191 return g_variant_new_string (object_path);
2192 } else if (g_strcmp0 (property_name, "DisplayName") == 0) {
2193 char *share_name = g_settings_get_string (plugin->settings, "share-name");
2194 if (share_name == NULL || share_name[0] == '\0') {
2195 g_free (share_name);
2196 share_name = g_strdup ("@REALNAME@'s Rhythmbox on @HOSTNAME@");
2197 }
2198 v = g_variant_new_string (share_name);
2199 g_free (share_name);
2200 return v;
2201 }
2202 } else if (g_strcmp0 (interface_name, MEDIA_SERVER2_CONTAINER_IFACE_NAME) == 0) {
2203 if (g_strcmp0 (property_name, "ChildCount") == 0 ||
2204 g_strcmp0 (property_name, "ContainerCount") == 0) {
2205 count = count_sources_by_parent (plugin, object_path);
2206 count += count_categories_by_parent (plugin, object_path);
2207 return g_variant_new_uint32 (count);
2208 } else if (g_strcmp0 (property_name, "ItemCount") == 0) {
2209 return g_variant_new_uint32 (0);
2210 } else if (g_strcmp0 (property_name, "Searchable") == 0) {
2211 return g_variant_new_boolean (FALSE);
2212 } else if (g_strcmp0 (property_name, "Icon") == 0) {
2213 /* XXX implement this, I guess */
2214 }
2215 }
2216 g_set_error (error,
2217 G_DBUS_ERROR,
2218 G_DBUS_ERROR_NOT_SUPPORTED,
2219 "Property %s.%s not supported",
2220 interface_name,
2221 property_name);
2222 return NULL;
2223 }
2224
2225 static void
2226 add_root_property (RBMediaServer2Plugin *plugin, GVariantBuilder *properties, const char *iface, const char *property)
2227 {
2228 GVariant *v;
2229 v = get_root_property (plugin->connection, NULL, RB_MEDIASERVER2_ROOT, iface, property, NULL, plugin);
2230 g_variant_builder_add (properties, "{sv}", property, v);
2231 }
2232
2233 static void
2234 emit_root_property_updates (RBMediaServer2Plugin *plugin)
2235 {
2236 GError *error = NULL;
2237 const char *invalidated[] = { NULL };
2238 GVariantBuilder *properties;
2239 GVariant *parameters;
2240
2241
2242 properties = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
2243 add_root_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ItemCount");
2244 add_root_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ChildCount");
2245 add_root_property (plugin, properties, MEDIA_SERVER2_CONTAINER_IFACE_NAME, "ContainerCount");
2246
2247 parameters = g_variant_new ("(sa{sv}^as)",
2248 MEDIA_SERVER2_CONTAINER_IFACE_NAME,
2249 properties,
2250 invalidated);
2251 g_variant_builder_unref (properties);
2252 g_dbus_connection_emit_signal (plugin->connection,
2253 NULL,
2254 RB_MEDIASERVER2_ROOT,
2255 "org.freedesktop.DBus.Properties",
2256 "PropertiesChanged",
2257 parameters,
2258 &error);
2259 if (error != NULL) {
2260 g_warning ("Unable to send property changes for MediaServer2 root container: %s",
2261 error->message);
2262 g_clear_error (&error);
2263 }
2264 }
2265
2266 static const GDBusInterfaceVTable root_vtable =
2267 {
2268 (GDBusInterfaceMethodCallFunc) root_method_call,
2269 (GDBusInterfaceGetPropertyFunc) get_root_property,
2270 NULL
2271 };
2272
2273 /* source watching */
2274
2275 static gboolean
2276 is_shareable_playlist (RBSource *source)
2277 {
2278 gboolean is_local;
2279
2280 if (RB_IS_PLAYLIST_SOURCE (source) == FALSE) {
2281 return FALSE;
2282 }
2283
2284 g_object_get (source, "is-local", &is_local, NULL);
2285 return is_local;
2286 }
2287
2288 /*
2289 * exposing a device source over dbus only makes sense once it's fully populated.
2290 * we don't currently have a way to determine that, so we don't share devices yet.
2291 */
2292 /*
2293 static gboolean
2294 is_shareable_device (RBSource *source)
2295 {
2296 return RB_IS_DEVICE_SOURCE (source);
2297 }
2298 */
2299
2300 static void
2301 display_page_inserted_cb (RBDisplayPageModel *model, RBDisplayPage *page, GtkTreeIter *iter, RBMediaServer2Plugin *plugin)
2302 {
2303 GList *l;
2304
2305 if (RB_IS_SOURCE (page)) {
2306 RBSource *source = RB_SOURCE (page);
2307 /* figure out if this is a source we can publish */
2308 for (l = plugin->categories; l != NULL; l = l->next) {
2309 CategoryRegistrationData *category_data = l->data;
2310
2311 if (category_data->match_source (source)) {
2312 char *dbus_path;
2313 dbus_path = g_strdup_printf ("%s/%" G_GINTPTR_FORMAT,
2314 category_data->dbus_path,
2315 (gintptr) source);
2316 rb_debug ("adding new source %s to category %s", dbus_path, category_data->name);
2317 register_source_container (plugin, source, dbus_path, category_data->dbus_path, TRUE);
2318 g_free (dbus_path);
2319 }
2320 }
2321 }
2322
2323 }
2324
2325 static gboolean
2326 display_page_foreach_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, RBMediaServer2Plugin *plugin)
2327 {
2328 RBDisplayPage *page;
2329
2330 gtk_tree_model_get (model, iter,
2331 RB_DISPLAY_PAGE_MODEL_COLUMN_PAGE, &page,
2332 -1);
2333 display_page_inserted_cb (RB_DISPLAY_PAGE_MODEL (model), page, iter, plugin);
2334
2335 g_object_unref (page);
2336 return FALSE;
2337 }
2338
2339 /* plugin */
2340
2341 static void
2342 name_acquired_cb (GDBusConnection *connection, const char *name, RBMediaServer2Plugin *plugin)
2343 {
2344 rb_debug ("acquired dbus name %s", name);
2345 }
2346
2347 static void
2348 name_lost_cb (GDBusConnection *connection, const char *name, RBMediaServer2Plugin *plugin)
2349 {
2350 rb_debug ("lost dbus name %s", name);
2351 }
2352
2353 static void
2354 impl_activate (PeasActivatable *bplugin)
2355 {
2356 RBMediaServer2Plugin *plugin;
2357 GDBusInterfaceInfo *container_iface;
2358 SourceRegistrationData *source_data;
2359 RBSource *source;
2360 GError *error = NULL;
2361 RBShell *shell;
2362
2363 rb_debug ("activating DBus MediaServer2 plugin");
2364
2365 plugin = RB_DBUS_MEDIA_SERVER_PLUGIN (bplugin);
2366 g_object_get (plugin, "object", &shell, NULL);
2367 g_object_get (shell,
2368 "db", &plugin->db,
2369 "display-page-model", &plugin->display_page_model,
2370 NULL);
2371
2372 plugin->settings = g_settings_new ("org.gnome.rhythmbox.sharing");
2373
2374 plugin->node_info = g_dbus_node_info_new_for_xml (media_server2_spec, &error);
2375 if (error != NULL) {
2376 g_warning ("Unable to parse MediaServer2 spec: %s", error->message);
2377 g_clear_error (&error);
2378 g_object_unref (shell);
2379 return;
2380 }
2381
2382 plugin->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
2383 if (error != NULL) {
2384 g_warning ("Unable to connect to D-Bus: %s", error->message);
2385 g_clear_error (&error);
2386 g_object_unref (shell);
2387 return;
2388 }
2389
2390 container_iface = g_dbus_node_info_lookup_interface (plugin->node_info, MEDIA_SERVER2_CONTAINER_IFACE_NAME);
2391
2392 /* register root */
2393 register_object (plugin, &root_vtable, container_iface, RB_MEDIASERVER2_ROOT, G_OBJECT (plugin), plugin->root_reg_id);
2394
2395 /* register fixed sources (library, podcasts, etc.) */
2396 g_object_get (shell, "library-source", &source, NULL);
2397 source_data = register_source_container (plugin, source, RB_MEDIASERVER2_LIBRARY, RB_MEDIASERVER2_ROOT, FALSE);
2398 register_property_container (plugin->connection, source_data, RHYTHMDB_PROP_ARTIST, _("Artists"));
2399 register_property_container (plugin->connection, source_data, RHYTHMDB_PROP_ALBUM, _("Albums"));
2400 register_property_container (plugin->connection, source_data, RHYTHMDB_PROP_GENRE, _("Genres"));
2401 /* year won't work yet */
2402 g_object_unref (source);
2403
2404 /* watch for user-creatable sources (playlists, devices) */
2405 g_signal_connect_object (plugin->display_page_model, "page-inserted", G_CALLBACK (display_page_inserted_cb), plugin, 0);
2406 gtk_tree_model_foreach (GTK_TREE_MODEL (plugin->display_page_model),
2407 (GtkTreeModelForeachFunc) display_page_foreach_cb,
2408 plugin);
2409 register_category_container (plugin, _("Playlists"), RB_MEDIASERVER2_PLAYLISTS, RB_MEDIASERVER2_ROOT, is_shareable_playlist);
2410 /* see comments above */
2411 /* register_category_container (plugin, _("Devices"), RB_MEDIASERVER2_DEVICES, RB_MEDIASERVER2_ROOT, is_shareable_device); */
2412
2413 /* register entry subtree */
2414 plugin->entry_reg_id = g_dbus_connection_register_subtree (plugin->connection,
2415 RB_MEDIASERVER2_ENTRY_SUBTREE,
2416 &entry_subtree_vtable,
2417 G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
2418 plugin,
2419 NULL,
2420 &error);
2421 if (error != NULL) {
2422 g_warning ("Unable to register MediaServer2 entry subtree: %s", error->message);
2423 g_clear_error (&error);
2424 g_object_unref (shell);
2425 return;
2426 }
2427
2428 plugin->name_own_id = g_bus_own_name (G_BUS_TYPE_SESSION,
2429 RB_MEDIASERVER2_BUS_NAME,
2430 G_BUS_NAME_OWNER_FLAGS_NONE,
2431 NULL,
2432 (GBusNameAcquiredCallback) name_acquired_cb,
2433 (GBusNameLostCallback) name_lost_cb,
2434 g_object_ref (plugin),
2435 g_object_unref);
2436 g_object_unref (shell);
2437 }
2438
2439 static void
2440 impl_deactivate (PeasActivatable *bplugin)
2441 {
2442 RBMediaServer2Plugin *plugin;
2443 GList *l;
2444
2445 plugin = RB_DBUS_MEDIA_SERVER_PLUGIN (bplugin);
2446
2447 if (plugin->emit_updated_id != 0) {
2448 g_source_remove (plugin->emit_updated_id);
2449 plugin->emit_updated_id = 0;
2450 }
2451
2452 /* unregister objects */
2453 unregister_object (plugin, plugin->root_reg_id);
2454
2455 for (l = plugin->sources; l != NULL; l = l->next) {
2456 unregister_source_container (plugin, l->data, TRUE);
2457 }
2458 rb_list_destroy_free (plugin->sources, (GDestroyNotify) destroy_registration_data);
2459 plugin->sources = NULL;
2460
2461 for (l = plugin->categories; l != NULL; l = l->next) {
2462 unregister_category_container (plugin, l->data, TRUE);
2463 }
2464 rb_list_destroy_free (plugin->categories, (GDestroyNotify) destroy_category_data);
2465 plugin->categories = NULL;
2466
2467 if (plugin->entry_reg_id != 0) {
2468 g_dbus_connection_unregister_subtree (plugin->connection, plugin->entry_reg_id);
2469 plugin->entry_reg_id = 0;
2470 }
2471
2472 if (plugin->settings != NULL) {
2473 g_object_unref (plugin->settings);
2474 plugin->settings = NULL;
2475 }
2476
2477 if (plugin->display_page_model != NULL) {
2478 g_signal_handlers_disconnect_by_func (plugin->display_page_model,
2479 display_page_inserted_cb,
2480 plugin);
2481 g_object_unref (plugin->display_page_model);
2482 plugin->display_page_model = NULL;
2483 }
2484
2485 if (plugin->db != NULL) {
2486 g_object_unref (plugin->db);
2487 plugin->db = NULL;
2488 }
2489
2490 if (plugin->name_own_id > 0) {
2491 g_bus_unown_name (plugin->name_own_id);
2492 plugin->name_own_id = 0;
2493 }
2494
2495 if (plugin->connection != NULL) {
2496 g_object_unref (plugin->connection);
2497 plugin->connection = NULL;
2498 }
2499 }
2500
2501
2502 G_MODULE_EXPORT void
2503 peas_register_types (PeasObjectModule *module)
2504 {
2505 rb_dbus_media_server_plugin_register_type (G_TYPE_MODULE (module));
2506 peas_object_module_register_extension_type (module,
2507 PEAS_TYPE_ACTIVATABLE,
2508 RB_TYPE_DBUS_MEDIA_SERVER_PLUGIN);
2509 }