Updated Macedonian Translation <ufo@linux.net.mk>
[rhythmbox.git] / sources / rb-daap-source.c
blob1ad118de0dba52de564ebaabc19d8fa605706acc
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Implementation of DAAP (iTunes Music Sharing) source object
5 * Copyright (C) 2005 Charles Schmidt <cschmidt2@emich.edu>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "config.h"
25 #include <string.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.h>
30 #include "rhythmdb.h"
31 #include "rb-shell.h"
32 #include "eel-gconf-extensions.h"
33 #include "rb-daap-source.h"
34 #include "rb-stock-icons.h"
35 #include "rb-debug.h"
36 #include "rb-util.h"
37 #include "rb-file-helpers.h"
38 #include "rb-dialog.h"
39 #include "rb-preferences.h"
41 #include "rb-daap-connection.h"
42 #include "rb-daap-mdns-browser.h"
43 #include "rb-daap-dialog.h"
45 #include "rb-static-playlist-source.h"
47 static void rb_daap_source_dispose (GObject *object);
48 static void rb_daap_source_set_property (GObject *object,
49 guint prop_id,
50 const GValue *value,
51 GParamSpec *pspec);
52 static void rb_daap_source_get_property (GObject *object,
53 guint prop_id,
54 GValue *value,
55 GParamSpec *pspec);
56 static void rb_daap_source_activate (RBSource *source);
58 static gboolean rb_daap_source_show_popup (RBSource *source);
59 static char * rb_daap_source_get_browser_key (RBSource *source);
60 static const char * rb_daap_source_get_paned_key (RBBrowserSource *source);
61 static void rb_daap_source_get_status (RBSource *source, char **text, char **progress_text, float *progress);
62 static void rb_daap_source_cmd_disconnect (GtkAction *action, RBShell *shell);
63 static void rb_daap_source_disconnect (RBDAAPSource *daap_source);
65 #define CONF_ENABLE_BROWSING CONF_PREFIX "/sharing/enable_browsing"
66 #define CONF_STATE_SORTING CONF_PREFIX "/state/daap/sorting"
67 #define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/daap/paned_position"
68 #define CONF_STATE_SHOW_BROWSER CONF_PREFIX "/state/daap/show_browser"
70 static RBDaapMdnsBrowser *mdns_browser = NULL;
72 static GHashTable *source_lookup = NULL;
74 static guint enable_browsing_notify_id = EEL_GCONF_UNDEFINED_CONNECTION;
76 static GdkPixbuf *daap_share_pixbuf = NULL;
77 static GdkPixbuf *daap_share_locked_pixbuf = NULL;
78 static gboolean daap_was_shutdown = FALSE;
80 struct RBDAAPSourcePrivate
82 GtkActionGroup *action_group;
84 char *service_name;
85 char *host;
86 guint port;
87 gboolean password_protected;
89 gpointer connection;
91 GSList *playlist_sources;
93 const char *connection_status;
94 float connection_progress;
96 gboolean disconnecting;
99 enum {
100 PROP_0,
101 PROP_SERVICE_NAME,
102 PROP_HOST,
103 PROP_PORT,
104 PROP_PASSWORD_PROTECTED
107 G_DEFINE_TYPE (RBDAAPSource, rb_daap_source, RB_TYPE_BROWSER_SOURCE)
109 static GtkActionEntry rb_daap_source_actions [] =
111 { "DaapSourceDisconnect", GTK_STOCK_DISCONNECT, N_("_Disconnect"), NULL,
112 N_("Disconnect from DAAP share"),
113 G_CALLBACK (rb_daap_source_cmd_disconnect) },
115 GtkActionGroup *daap_action_group;
116 guint daap_ui_merge_id;
118 static void
119 rb_daap_source_dispose (GObject *object)
121 RBDAAPSource *source = RB_DAAP_SOURCE (object);
123 /* we should already have been disconnected */
124 g_assert (source->priv->connection == NULL);
126 G_OBJECT_CLASS (rb_daap_source_parent_class)->dispose (object);
129 static void
130 rb_daap_source_finalize (GObject *object)
132 RBDAAPSource *source = RB_DAAP_SOURCE (object);
134 g_free (source->priv->service_name);
135 g_free (source->priv->host);
137 G_OBJECT_CLASS (rb_daap_source_parent_class)->finalize (object);
140 static void
141 rb_daap_source_class_init (RBDAAPSourceClass *klass)
143 GObjectClass *object_class = G_OBJECT_CLASS (klass);
144 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
145 RBBrowserSourceClass *browser_source_class = RB_BROWSER_SOURCE_CLASS (klass);
147 object_class->dispose = rb_daap_source_dispose;
148 object_class->finalize = rb_daap_source_finalize;
149 object_class->get_property = rb_daap_source_get_property;
150 object_class->set_property = rb_daap_source_set_property;
152 source_class->impl_activate = rb_daap_source_activate;
153 source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
154 source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
155 source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
156 source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
157 source_class->impl_paste = NULL;
158 source_class->impl_receive_drag = NULL;
159 source_class->impl_delete = NULL;
160 source_class->impl_show_popup = rb_daap_source_show_popup;
161 source_class->impl_get_config_widget = NULL;
162 source_class->impl_get_browser_key = rb_daap_source_get_browser_key;
163 source_class->impl_get_status = rb_daap_source_get_status;
165 browser_source_class->impl_get_paned_key = rb_daap_source_get_paned_key;
166 browser_source_class->impl_has_drop_support = (RBBrowserSourceFeatureFunc) rb_false_function;
168 g_object_class_install_property (object_class,
169 PROP_SERVICE_NAME,
170 g_param_spec_string ("service-name",
171 "Service name",
172 "mDNS/DNS-SD service name of the share",
173 NULL,
174 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
176 g_object_class_install_property (object_class,
177 PROP_HOST,
178 g_param_spec_string ("host",
179 "Host",
180 "Host IP address",
181 NULL,
182 G_PARAM_READWRITE));
184 g_object_class_install_property (object_class,
185 PROP_PORT,
186 g_param_spec_uint ("port",
187 "Port",
188 "Port of DAAP server on host",
190 G_MAXUINT,
192 G_PARAM_READWRITE));
193 g_object_class_install_property (object_class,
194 PROP_PASSWORD_PROTECTED,
195 g_param_spec_boolean ("password-protected",
196 "Password Protected",
197 "Whether the share is password protected",
198 FALSE,
199 G_PARAM_READWRITE));
201 g_type_class_add_private (klass, sizeof (RBDAAPSourcePrivate));
204 static void
205 rb_daap_source_init (RBDAAPSource *source)
207 source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source,
208 RB_TYPE_DAAP_SOURCE,
209 RBDAAPSourcePrivate);
212 static void
213 rb_daap_source_set_property (GObject *object,
214 guint prop_id,
215 const GValue *value,
216 GParamSpec *pspec)
218 RBDAAPSource *source = RB_DAAP_SOURCE (object);
220 switch (prop_id) {
221 case PROP_SERVICE_NAME:
222 source->priv->service_name = g_value_dup_string (value);
223 break;
224 case PROP_HOST:
225 if (source->priv->host) {
226 g_free (source->priv->host);
228 source->priv->host = g_value_dup_string (value);
229 /* FIXME what do we do if its already connected and we
230 * get a new host? */
231 break;
232 case PROP_PORT:
233 source->priv->port = g_value_get_uint (value);
234 break;
235 case PROP_PASSWORD_PROTECTED:
236 source->priv->password_protected = g_value_get_boolean (value);
237 break;
238 default:
239 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
240 break;
244 static void
245 rb_daap_source_get_property (GObject *object,
246 guint prop_id,
247 GValue *value,
248 GParamSpec *pspec)
250 RBDAAPSource *source = RB_DAAP_SOURCE (object);
252 switch (prop_id) {
253 case PROP_SERVICE_NAME:
254 g_value_set_string (value, source->priv->service_name);
255 break;
256 case PROP_HOST:
257 g_value_set_string (value, source->priv->host);
258 break;
259 case PROP_PORT:
260 g_value_set_uint (value, source->priv->port);
261 break;
262 case PROP_PASSWORD_PROTECTED:
263 g_value_set_boolean (value, source->priv->password_protected);
264 break;
265 default:
266 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267 break;
271 static GdkPixbuf *
272 rb_daap_get_icon (gboolean password_protected,
273 gboolean connected)
275 GdkPixbuf *icon;
277 g_return_val_if_fail (daap_share_pixbuf != NULL, NULL);
278 g_return_val_if_fail (daap_share_locked_pixbuf != NULL, NULL);
280 if (! password_protected) {
281 icon = g_object_ref (daap_share_pixbuf);
282 } else if (connected) {
283 icon = g_object_ref (daap_share_pixbuf);
284 } else {
285 icon = g_object_ref (daap_share_locked_pixbuf);
288 return icon;
291 static RBSource *
292 rb_daap_source_new (RBShell *shell,
293 const char *service_name,
294 const char *name,
295 const char *host,
296 guint port,
297 gboolean password_protected)
299 RBSource *source;
300 RhythmDBEntryType type;
301 GdkPixbuf *icon;
302 RhythmDB *db;
304 g_object_get (shell, "db", &db, NULL);
305 type = rhythmdb_entry_register_type (db, NULL);
306 g_object_unref (db);
308 icon = rb_daap_get_icon (password_protected, FALSE);
309 source = RB_SOURCE (g_object_new (RB_TYPE_DAAP_SOURCE,
310 "service-name", service_name,
311 "name", name,
312 "host", host,
313 "port", port,
314 "entry-type", type,
315 "icon", icon,
316 "shell", shell,
317 "visibility", TRUE,
318 "sorting-key", CONF_STATE_SORTING,
319 "password-protected", password_protected,
320 "sourcelist-group", RB_SOURCELIST_GROUP_TRANSIENT,
321 NULL));
323 if (icon != NULL) {
324 g_object_unref (icon);
327 rb_shell_register_entry_type_for_source (shell, source,
328 type);
330 return source;
333 static RBSource *
334 find_source_by_service_name (const char *service_name)
336 RBSource *source;
338 source = g_hash_table_lookup (source_lookup, service_name);
340 return source;
343 static void
344 mdns_service_added (RBDaapMdnsBrowser *browser,
345 const char *service_name,
346 const char *name,
347 const char *host,
348 guint port,
349 gboolean password_protected,
350 RBShell *shell)
352 RBSource *source;
354 #if 0
355 g_message ("New service: %s name=%s host=%s port=%u password=%d",
356 service_name, name, host, port, password_protected);
357 #endif
359 source = find_source_by_service_name (service_name);
361 if (source == NULL) {
362 source = rb_daap_source_new (shell, service_name, name, host, port, password_protected);
363 g_hash_table_insert (source_lookup, g_strdup (service_name), source);
364 rb_shell_append_source (shell, source, NULL);
365 } else {
366 g_object_set (G_OBJECT (source),
367 "name", name,
368 "host", host,
369 "port", port,
370 "password-protected", password_protected,
371 NULL);
375 static void
376 mdns_service_removed (RBDaapMdnsBrowser *browser,
377 const char *service_name,
378 RBShell *shell)
380 RBSource *source;
382 source = find_source_by_service_name (service_name);
384 rb_debug ("DAAP source '%s' went away", service_name);
385 if (source == NULL) {
386 return;
389 g_hash_table_remove (source_lookup, service_name);
392 static void
393 remove_source (RBSource *source)
395 rb_debug ("Removing DAAP source: %s", RB_DAAP_SOURCE (source)->priv->service_name);
396 rb_daap_source_disconnect (RB_DAAP_SOURCE (source));
397 rb_source_delete_thyself (source);
400 static void
401 start_browsing (RBShell *shell)
403 GError *error;
405 if (mdns_browser != NULL) {
406 return;
409 mdns_browser = rb_daap_mdns_browser_new ();
410 if (mdns_browser == NULL) {
411 g_warning ("Unable to start mDNS browsing");
412 return;
415 g_signal_connect_object (mdns_browser,
416 "service-added",
417 G_CALLBACK (mdns_service_added),
418 shell,
420 g_signal_connect_object (mdns_browser,
421 "service-removed",
422 G_CALLBACK (mdns_service_removed),
423 shell,
426 error = NULL;
427 rb_daap_mdns_browser_start (mdns_browser, &error);
428 if (error != NULL) {
429 g_warning ("Unable to start mDNS browsing: %s", error->message);
430 g_error_free (error);
433 source_lookup = g_hash_table_new_full ((GHashFunc)g_str_hash,
434 (GEqualFunc)g_str_equal,
435 (GDestroyNotify)g_free,
436 (GDestroyNotify)remove_source);
440 static void
441 stop_browsing (RBShell *shell)
443 GError *error;
445 if (mdns_browser == NULL) {
446 return;
449 rb_debug ("Destroying DAAP source lookup");
451 g_hash_table_destroy (source_lookup);
452 source_lookup = NULL;
454 g_signal_handlers_disconnect_by_func (mdns_browser, mdns_service_added, shell);
455 g_signal_handlers_disconnect_by_func (mdns_browser, mdns_service_removed, shell);
457 error = NULL;
458 rb_daap_mdns_browser_stop (mdns_browser, &error);
459 if (error != NULL) {
460 g_warning ("Unable to stop mDNS browsing: %s", error->message);
461 g_error_free (error);
464 g_object_unref (mdns_browser);
465 mdns_browser = NULL;
468 static void
469 enable_browsing_changed_cb (GConfClient *client,
470 guint cnxn_id,
471 GConfEntry *entry,
472 RBShell *shell)
474 gboolean enabled = eel_gconf_get_boolean (CONF_ENABLE_BROWSING);
476 if (enabled) {
477 start_browsing (shell);
478 } else {
479 stop_browsing (shell);
483 static GdkPixbuf *
484 composite_icons (const GdkPixbuf *src1,
485 const GdkPixbuf *src2)
487 GdkPixbuf *dest;
488 GdkPixbuf *scaled;
489 gint w1, w2, h1, h2;
490 gint dest_x, dest_y;
491 gboolean do_scale;
493 if (! src1) {
494 return NULL;
497 dest = gdk_pixbuf_copy (src1);
499 if (! src2) {
500 return dest;
503 w1 = gdk_pixbuf_get_width (src1);
504 h1 = gdk_pixbuf_get_height (src1);
505 w2 = gdk_pixbuf_get_width (src2);
506 h2 = gdk_pixbuf_get_height (src2);
508 do_scale = ((float)w1 * 0.8) < w2;
510 /* scale the emblem down if it will obscure the entire bottom image */
511 if (do_scale) {
512 scaled = gdk_pixbuf_scale_simple (src2, w1 / 2, h1 / 2, GDK_INTERP_BILINEAR);
513 } else {
514 scaled = (GdkPixbuf *)src2;
517 w2 = gdk_pixbuf_get_width (scaled);
518 h2 = gdk_pixbuf_get_height (scaled);
520 dest_x = w1 - w2;
521 dest_y = h1 - h2;
523 gdk_pixbuf_composite (scaled, dest,
524 dest_x, dest_y,
525 w2, h2,
526 dest_x, dest_y,
527 1.0, 1.0,
528 GDK_INTERP_BILINEAR, 0xFF);
530 if (do_scale) {
531 g_object_unref (scaled);
534 return dest;
537 static void
538 create_pixbufs (void)
540 GdkPixbuf *emblem;
541 GtkIconTheme *theme;
542 gint size;
544 theme = gtk_icon_theme_get_default ();
546 gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
547 daap_share_pixbuf = gtk_icon_theme_load_icon (theme, "gnome-fs-network", size, 0, NULL);
549 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &size, NULL);
550 emblem = gtk_icon_theme_load_icon (theme, "stock_lock", size, 0, NULL);
552 daap_share_locked_pixbuf = composite_icons (daap_share_pixbuf, emblem);
554 if (emblem != NULL) {
555 g_object_unref (emblem);
559 static void
560 destroy_pixbufs (void)
562 if (daap_share_pixbuf != NULL) {
563 g_object_unref (daap_share_pixbuf);
566 daap_share_pixbuf = NULL;
568 if (daap_share_locked_pixbuf != NULL) {
569 g_object_unref (daap_share_locked_pixbuf);
571 daap_share_locked_pixbuf = NULL;
574 RBSource *
575 rb_daap_sources_init (RBShell *shell)
577 gboolean enabled = TRUE;
578 GConfValue *value;
579 GConfClient *client = eel_gconf_client_get_global ();
580 GtkUIManager *uimanager = NULL;
582 value = gconf_client_get_without_default (client,
583 CONF_ENABLE_BROWSING, NULL);
584 if (value != NULL) {
585 enabled = gconf_value_get_bool (value);
586 gconf_value_free (value);
589 g_object_ref (shell);
591 if (enabled) {
592 start_browsing (shell);
595 enable_browsing_notify_id =
596 eel_gconf_notification_add (CONF_ENABLE_BROWSING,
597 (GConfClientNotifyFunc) enable_browsing_changed_cb,
598 shell);
600 create_pixbufs ();
602 /* add UI */
603 g_object_get (shell,
604 "ui-manager", &uimanager,
605 NULL);
606 daap_action_group = gtk_action_group_new ("DaapActions");
607 gtk_action_group_set_translation_domain (daap_action_group,
608 GETTEXT_PACKAGE);
609 gtk_action_group_add_actions (daap_action_group,
610 rb_daap_source_actions, G_N_ELEMENTS (rb_daap_source_actions),
611 shell);
612 gtk_ui_manager_insert_action_group (uimanager, daap_action_group, 0);
613 daap_ui_merge_id = gtk_ui_manager_add_ui_from_file (uimanager,
614 rb_file ("daap-ui.xml"),
615 NULL);
616 g_object_unref (uimanager);
618 return NULL;
621 void
622 rb_daap_sources_shutdown (RBShell *shell)
624 GtkUIManager *uimanager = NULL;
626 rb_debug ("Shutting down DAAP sources");
628 g_assert (!daap_was_shutdown);
629 daap_was_shutdown = TRUE;
631 g_object_get (shell,
632 "ui-manager", &uimanager,
633 NULL);
635 if (mdns_browser) {
636 stop_browsing (shell);
639 if (enable_browsing_notify_id != EEL_GCONF_UNDEFINED_CONNECTION) {
640 eel_gconf_notification_remove (enable_browsing_notify_id);
641 enable_browsing_notify_id = EEL_GCONF_UNDEFINED_CONNECTION;
644 gtk_ui_manager_remove_ui (uimanager, daap_ui_merge_id);
645 gtk_ui_manager_remove_action_group (uimanager, daap_action_group);
647 g_object_unref (uimanager);
648 g_object_unref (shell);
650 destroy_pixbufs ();
653 static char *
654 connection_auth_cb (RBDAAPConnection *connection,
655 const char *name,
656 RBSource *source)
658 char *password;
659 GtkWindow *parent;
661 parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (source)));
662 password = rb_daap_password_dialog_new_run (parent, name);
664 return password;
667 static void
668 connection_connecting_cb (RBDAAPConnection *connection,
669 RBDAAPConnectionState state,
670 float progress,
671 RBDAAPSource *source)
673 GdkPixbuf *icon;
674 gboolean is_connected;
676 rb_debug ("DAAP connection status: %d/%f", state, progress);
678 switch (state) {
679 case DAAP_GET_INFO:
680 case DAAP_GET_PASSWORD:
681 case DAAP_LOGIN:
682 source->priv->connection_status = _("Connecting to music share");
683 break;
684 case DAAP_GET_REVISION_NUMBER:
685 case DAAP_GET_DB_INFO:
686 case DAAP_GET_SONGS:
687 case DAAP_GET_PLAYLISTS:
688 case DAAP_GET_PLAYLIST_ENTRIES:
689 source->priv->connection_status = _("Retrieving songs from music share");
690 break;
691 case DAAP_LOGOUT:
692 case DAAP_DONE:
693 source->priv->connection_status = NULL;
694 break;
697 source->priv->connection_progress = progress;
699 rb_source_notify_status_changed (RB_SOURCE (source));
701 is_connected = rb_daap_connection_is_connected (connection);
702 icon = rb_daap_get_icon (source->priv->password_protected, is_connected);
703 g_object_set (source, "icon", icon, NULL);
704 if (icon != NULL) {
705 g_object_unref (icon);
709 static void
710 connection_disconnected_cb (RBDAAPConnection *connection,
711 RBDAAPSource *source)
713 GdkPixbuf *icon;
715 rb_debug ("DAAP connection disconnected");
717 if (daap_was_shutdown) {
718 return;
721 icon = rb_daap_get_icon (source->priv->password_protected, FALSE);
722 g_object_set (source, "icon", icon, NULL);
723 if (icon != NULL) {
724 g_object_unref (icon);
728 static void
729 release_connection (RBDAAPSource *daap_source)
731 rb_debug ("Releasing connection");
733 g_object_unref (G_OBJECT (daap_source->priv->connection));
734 /* Weak pointer will set to NULL for us */
735 /*daap_source->priv->connection = NULL;*/
738 static void
739 rb_daap_source_connection_cb (RBDAAPConnection *connection,
740 gboolean result,
741 const char *reason,
742 RBSource *source)
744 RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
745 RBShell *shell = NULL;
746 GSList *playlists;
747 GSList *l;
748 RhythmDBEntryType entry_type;
750 rb_debug ("Connection callback result: %s", result ? "success" : "failure");
752 if (result == FALSE) {
753 if (reason != NULL) {
754 rb_error_dialog (NULL, _("Could not connect to shared music"), "%s", reason);
757 /* Don't release the connection if we are already disconnecting */
758 if (! daap_source->priv->disconnecting) {
759 release_connection (daap_source);
762 return;
765 g_object_get (daap_source,
766 "shell", &shell,
767 "entry-type", &entry_type,
768 NULL);
769 playlists = rb_daap_connection_get_playlists (RB_DAAP_CONNECTION (daap_source->priv->connection));
770 for (l = playlists; l != NULL; l = g_slist_next (l)) {
771 RBDAAPPlaylist *playlist = l->data;
772 RBSource *playlist_source;
774 playlist_source = rb_static_playlist_source_new (shell, playlist->name, FALSE, entry_type);
775 rb_static_playlist_source_add_locations (RB_STATIC_PLAYLIST_SOURCE (playlist_source), playlist->uris);
777 rb_shell_append_source (shell, playlist_source, RB_SOURCE (daap_source));
778 daap_source->priv->playlist_sources = g_slist_prepend (daap_source->priv->playlist_sources, playlist_source);
781 g_object_unref (shell);
782 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
785 static void
786 rb_daap_source_activate (RBSource *source)
788 RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
789 RBShell *shell = NULL;
790 RhythmDB *db = NULL;
791 char *name = NULL;
792 RhythmDBEntryType type;
794 if (daap_source->priv->connection != NULL) {
795 return;
798 g_object_get (daap_source,
799 "shell", &shell,
800 "entry-type", &type,
801 "name", &name,
802 NULL);
803 g_object_get (shell, "db", &db, NULL);
805 daap_source->priv->connection = rb_daap_connection_new (name,
806 daap_source->priv->host,
807 daap_source->priv->port,
808 daap_source->priv->password_protected,
810 type);
811 g_object_add_weak_pointer (G_OBJECT (daap_source->priv->connection), (gpointer *)&daap_source->priv->connection);
813 rb_daap_connection_connect (RB_DAAP_CONNECTION (daap_source->priv->connection),
814 (RBDAAPConnectionCallback) rb_daap_source_connection_cb,
815 source);
817 g_object_unref (db);
818 g_object_unref (shell);
819 g_free (name);
821 g_signal_connect (daap_source->priv->connection,
822 "authenticate",
823 G_CALLBACK (connection_auth_cb),
824 source);
825 g_signal_connect (daap_source->priv->connection,
826 "connecting",
827 G_CALLBACK (connection_connecting_cb),
828 source);
829 g_signal_connect (daap_source->priv->connection,
830 "disconnected",
831 G_CALLBACK (connection_disconnected_cb),
832 source);
835 static void
836 rb_daap_source_disconnect_cb (RBDAAPConnection *connection,
837 gboolean result,
838 const char *reason,
839 RBSource *source)
841 RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
843 rb_debug ("DAAP source disconnected");
845 release_connection (daap_source);
847 g_object_unref (source);
850 static void
851 rb_daap_source_cmd_disconnect (GtkAction *action,
852 RBShell *shell)
854 RBSource *source;
856 g_object_get (shell,
857 "selected-source", &source,
858 NULL);
860 if (!RB_IS_DAAP_SOURCE (source)) {
861 g_critical ("got non-Daap source for Daap action");
862 return;
865 rb_daap_source_disconnect (RB_DAAP_SOURCE (source));
867 if (source != NULL) {
868 g_object_unref (source);
872 static void
873 rb_daap_source_disconnect (RBDAAPSource *daap_source)
875 if (daap_source->priv->connection) {
876 GSList *l;
877 RBShell *shell;
878 RhythmDB *db;
879 RhythmDBEntryType type;
881 rb_debug ("Disconnecting source");
883 daap_source->priv->disconnecting = TRUE;
885 g_object_get (daap_source, "shell", &shell, "entry-type", &type, NULL);
886 g_object_get (shell, "db", &db, NULL);
887 g_object_unref (shell);
889 rhythmdb_entry_delete_by_type (db, type);
890 rhythmdb_commit (db);
892 g_object_unref (db);
894 for (l = daap_source->priv->playlist_sources; l!= NULL; l = l->next) {
895 RBSource *playlist_source = RB_SOURCE (l->data);
897 rb_source_delete_thyself (playlist_source);
900 g_slist_free (daap_source->priv->playlist_sources);
901 daap_source->priv->playlist_sources = NULL;
903 /* we don't want these firing while we are disconnecting */
904 g_signal_handlers_disconnect_by_func (daap_source->priv->connection,
905 G_CALLBACK (connection_connecting_cb),
906 daap_source);
907 g_signal_handlers_disconnect_by_func (daap_source->priv->connection,
908 G_CALLBACK (connection_auth_cb),
909 daap_source);
911 /* keep the source alive until the disconnect completes */
912 g_object_ref (daap_source);
913 rb_daap_connection_disconnect (daap_source->priv->connection,
914 (RBDAAPConnectionCallback) rb_daap_source_disconnect_cb,
915 daap_source);
917 /* wait until disconnected */
918 rb_debug ("Waiting for DAAP connection to finish");
919 while (daap_source->priv->connection != NULL) {
920 rb_debug ("Waiting for DAAP connection to finish...");
921 gtk_main_iteration ();
926 static gboolean
927 rb_daap_source_show_popup (RBSource *source)
929 _rb_source_show_popup (RB_SOURCE (source), "/DAAPSourcePopup");
930 return TRUE;
933 static gboolean
934 source_host_find (const char *key,
935 RBDAAPSource *source,
936 const char *host)
938 if (source == NULL || host == NULL) {
939 return FALSE;
942 if (strcmp (host, source->priv->host) == 0) {
943 return TRUE;
946 return FALSE;
949 RBDAAPSource *
950 rb_daap_source_find_for_uri (const char *uri)
952 char *ip;
953 char *s;
954 RBDAAPSource *source = NULL;
956 if (uri == NULL) {
957 return NULL;
960 ip = strdup (uri + 7); /* daap:// */
961 s = strchr (ip, ':');
962 *s = '\0';
964 source = (RBDAAPSource *)g_hash_table_find (source_lookup, (GHRFunc)source_host_find, ip);
966 g_free (ip);
968 return source;
971 char *
972 rb_daap_source_get_headers (RBDAAPSource *source,
973 const char *uri,
974 glong time,
975 gint64 *bytes_out)
977 gint64 bytes = 0;
979 if (bytes_out != NULL) {
980 *bytes_out = 0;
983 /* If there is no connection then bail */
984 if (source->priv->connection == NULL) {
985 return NULL;
988 if (time != 0) {
989 RhythmDB *db;
990 RBShell *shell;
991 RhythmDBEntry *entry;
992 gulong bitrate;
994 g_object_get (source, "shell", &shell, NULL);
995 g_object_get (shell, "db", &db, NULL);
997 entry = rhythmdb_entry_lookup_by_location (db, uri);
999 g_object_unref (shell);
1000 g_object_unref (db);
1002 bitrate = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE);
1003 /* bitrate is kilobits per second */
1004 /* a kilobit is 128 bytes */
1005 bytes = (time * bitrate) * 128;
1008 if (bytes_out != NULL) {
1009 *bytes_out = bytes;
1012 return rb_daap_connection_get_headers (source->priv->connection, uri, bytes);
1015 static char *
1016 rb_daap_source_get_browser_key (RBSource *source)
1018 return g_strdup (CONF_STATE_SHOW_BROWSER);
1021 static const char *
1022 rb_daap_source_get_paned_key (RBBrowserSource *source)
1024 return CONF_STATE_PANED_POSITION;
1027 static void
1028 rb_daap_source_get_status (RBSource *source,
1029 char **text,
1030 char **progress_text,
1031 float *progress)
1033 RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
1035 if (text != NULL) {
1036 *text = NULL;
1038 if (progress_text != NULL) {
1039 *progress_text = NULL;
1041 if (progress != NULL) {
1042 *progress = 0.0;
1045 if (daap_source->priv->connection_status != NULL) {
1046 if (text != NULL) {
1047 *text = g_strdup (daap_source->priv->connection_status);
1050 if (progress != NULL) {
1051 *progress = daap_source->priv->connection_progress;
1054 return;
1057 RB_SOURCE_CLASS (rb_daap_source_parent_class)->impl_get_status (source, text, progress_text, progress);