? memcheck.7247
? sources/rb-generic-player-source.c
? sources/rb-generic-player-source.h
? sources/rb-njb-source.c
? sources/rb-njb-source.h
Index: configure.ac
===================================================================
RCS file: /cvs/gnome/rhythmbox/configure.ac,v
retrieving revision 1.201
diff -u -u -r1.201 configure.ac
--- configure.ac	10 Jan 2006 06:24:46 -0000	1.201
+++ configure.ac	16 Jan 2006 02:09:09 -0000
@@ -101,6 +101,30 @@
 fi			  
 AM_CONDITIONAL(USE_IPOD, test x"$use_ipod" = xyes)
 
+dnl NJB-player support
+
+AC_ARG_WITH(njb,
+            AC_HELP_STRING([--with-njb],
+			   [Enable NJB-based audio player support]),,
+	      with_njb=auto)
+if test "x$with_njb" != "xno"; then
+	PKG_CHECK_MODULES(NJB, libnjb, have_libnjb=yes, have_libnjb=no)
+	if test "x$have_libnjb" = "xno" -a "x$with_njb" = "xyes"; then
+	  AC_MSG_ERROR([NJB support explicitly requested but libnjb couldn't be found])
+	fi
+	if test "x$enable_hal05" = "xno" -a "x$with_njb" = "xyes"; then
+	  AC_MSG_ERROR([NJB support explicitly requested but HAL >= 0.5 couldn't be found])
+	fi
+	if test "x$have_libnjb" = "xyes" -a "x$enable_hal05" = "xyes"; then
+	  AC_DEFINE(WITH_NJB_SUPPORT, 1, [Define if NJB support is enabled])
+	  use_njb=yes
+	  AC_SUBST(NJB_CFLAGS)
+	  AC_SUBST(NJB_LIBS)
+	fi
+fi			  
+AM_CONDITIONAL(USE_NJB, test x"$use_njb" = xyes)
+
+
 dnl Database
 AC_ARG_WITH(database,
               AC_HELP_STRING([--with-database=tree|libgda],
@@ -722,6 +746,11 @@
 	AC_MSG_NOTICE([** iPod integration enabled])
 else
 	AC_MSG_NOTICE([   iPod integration disabled])
+fi
+if test x"$use_njb" = xyes; then
+	AC_MSG_NOTICE([** NJB integration enabled])
+else
+	AC_MSG_NOTICE([   NJB integration disabled])
 fi
 if test x"$enable_daap" = xyes; then
 	AC_MSG_NOTICE([** DAAP (music sharing) support is enabled])
Index: shell/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/Makefile.am,v
retrieving revision 1.66
diff -u -u -r1.66 Makefile.am
--- shell/Makefile.am	4 Jan 2006 09:50:16 -0000	1.66
+++ shell/Makefile.am	16 Jan 2006 02:09:17 -0000
@@ -116,6 +116,10 @@
 rhythmbox_LDADD += $(IPOD_LIBS)
 endif
 
+if USE_NJB
+rhythmbox_LDADD += $(NJB_LIBS)
+endif
+
 
 # i hate link order problems
 rhythmbox_LDADD += $(top_builddir)/remote/librbremote.la
Index: shell/rb-removable-media-manager.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-removable-media-manager.c,v
retrieving revision 1.11
diff -u -u -r1.11 rb-removable-media-manager.c
--- shell/rb-removable-media-manager.c	15 Jan 2006 01:25:08 -0000	1.11
+++ shell/rb-removable-media-manager.c	16 Jan 2006 02:09:18 -0000
@@ -34,6 +34,12 @@
 #ifdef WITH_IPOD_SUPPORT
 #include "rb-ipod-source.h"
 #endif
+#ifdef WITH_NJB_SUPPORT
+#include "rb-njb-source.h"
+#endif
+#ifdef HAVE_HAL_0_5
+#include "hal/libhal.h"
+#endif
 
 #include "rb-debug.h"
 #include "rb-dialog.h"
@@ -76,6 +82,10 @@
 				GnomeVFSVolume *volume, 
 				gpointer data);
 static void rb_removable_media_manager_scan (RBRemovableMediaManager *manager);
+static void rb_removable_media_manager_hal_device_added_cb (LibHalContext *ctx,
+							    const char *udi);
+static void rb_removable_media_manager_hal_device_removed_cb (LibHalContext *ctx,
+							      const char *udi);
 
 typedef struct
 {
@@ -97,9 +107,15 @@
 	GtkUIManager *uimanager;
 
 	GList *sources;
-	GHashTable *volume_mapping;
+	GHashTable *udi_source_mapping;
+	GHashTable *udi_volume_mapping;
+	GHashTable *volume_source_mapping;
 	GHashTable *cd_drive_mapping;
 	GList *cur_volume_list;
+
+#ifdef HAVE_HAL_0_5
+	LibHalContext *hal_ctx;
+#endif
 } RBRemovableMediaManagerPrivate;
 
 G_DEFINE_TYPE (RBRemovableMediaManager, rb_removable_media_manager, G_TYPE_OBJECT)
@@ -184,7 +200,11 @@
 {
 	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
 
-	priv->volume_mapping = g_hash_table_new (NULL, NULL);
+	priv->volume_source_mapping = g_hash_table_new (NULL, NULL);
+	priv->udi_volume_mapping = g_hash_table_new_full (g_str_hash, g_str_equal,
+							  g_free, NULL);
+	priv->udi_source_mapping = g_hash_table_new_full (g_str_hash, g_str_equal,
+							  g_free, NULL);
 }
 
 static void
@@ -209,15 +229,31 @@
 		g_list_free (priv->sources);
 		priv->sources = NULL;
 	}
-	if (priv->volume_mapping) {
-		g_hash_table_destroy (priv->volume_mapping);
-		priv->volume_mapping = NULL;
+	if (priv->udi_volume_mapping) {
+		g_hash_table_destroy (priv->udi_volume_mapping);
+		priv->udi_volume_mapping = NULL;
+	}
+	if (priv->udi_source_mapping) {
+		g_hash_table_destroy (priv->udi_source_mapping);
+		priv->udi_source_mapping = NULL;
+	}
+	if (priv->volume_source_mapping) {
+		g_hash_table_destroy (priv->volume_source_mapping);
+		priv->volume_source_mapping = NULL;
 	}
 	if (priv->cd_drive_mapping) {
 		g_hash_table_destroy (priv->cd_drive_mapping);
 		priv->cd_drive_mapping = NULL;
 	}
 
+	if (priv->hal_ctx) {
+		DBusError error;
+
+		/* FIXME: check the error */
+		libhal_ctx_shutdown (priv->hal_ctx, &error);
+		libhal_ctx_free (priv->hal_ctx);
+	}
+
 	priv->disposed = TRUE;
 
 	G_OBJECT_CLASS (rb_removable_media_manager_parent_class)->dispose (object);
@@ -361,6 +397,9 @@
 	GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor ();
 	GList *drives;
 
+	/* if this isn't NULL then we have already done the media load */
+	g_assert (priv->cd_drive_mapping == NULL);
+	
 	/*
 	 * Monitor new (un)mounted file systems to look for new media
 	 *
@@ -390,6 +429,49 @@
 	g_list_foreach (drives, (GFunc)begin_cd_drive_monitor, manager);
 	g_list_free (drives);
 
+#ifdef HAVE_HAL_0_5
+	/* init HAL */
+	{
+		DBusConnection *conn = NULL;
+		DBusError error;
+
+		dbus_error_init (&error);
+		priv->hal_ctx = libhal_ctx_new ();
+		if (priv->hal_ctx == NULL) {
+			rb_debug ("Error: %s\n", error.message);
+			goto hal_end;
+		}
+		conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+		if (conn == NULL) {
+			rb_debug ("Error: %s\n", error.message);
+			goto hal_end;
+		}
+		libhal_ctx_set_dbus_connection (priv->hal_ctx, conn);
+		if (!libhal_ctx_init (priv->hal_ctx, &error)) {
+			rb_debug ("Error: %s\n", error.message);
+			goto hal_end;
+		}
+
+		if (!libhal_ctx_set_user_data (priv->hal_ctx, manager)) {
+			rb_debug ("could not set HAL context user data");
+			goto hal_end;
+		}
+
+		/* begin HAL monitoring */
+		if (!libhal_ctx_set_device_added (priv->hal_ctx, rb_removable_media_manager_hal_device_added_cb)) {
+			rb_debug ("could not add HAL device-added callback");
+			goto hal_end;
+		}
+		if (!libhal_ctx_set_device_removed (priv->hal_ctx, rb_removable_media_manager_hal_device_removed_cb)) {
+			rb_debug ("could not add HAL device-removed callback");
+			goto hal_end;
+		}
+
+hal_end:
+		dbus_error_free (&error);
+	}
+#endif	
+
 	/* scan for media */
 	rb_removable_media_manager_scan (manager);
 
@@ -424,14 +506,16 @@
 {
 	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
 	RBRemovableMediaSource *source = NULL;
-	RBShell *shell;
 	char *fs_type, *device_path, *display_name, *hal_udi, *icon_name;
 	GnomeVFSDeviceType device_type;
 
 	g_assert (volume != NULL);
 	
-	if (g_hash_table_lookup (priv->volume_mapping, volume) != NULL)
+	hal_udi = gnome_vfs_volume_get_hal_udi (volume);
+	if (hal_udi && g_hash_table_lookup (priv->udi_source_mapping, hal_udi) != NULL) {
+		g_free (hal_udi);
 		return;
+	}
 
 	/* ignore network volumes */
 	device_type = gnome_vfs_volume_get_device_type (volume);
@@ -441,12 +525,9 @@
 	    device_type == GNOME_VFS_DEVICE_TYPE_NETWORK)
 		return;
 
-	g_object_get (G_OBJECT (mgr), "shell", &shell, NULL);
-
 	fs_type = gnome_vfs_volume_get_filesystem_type (volume);
 	device_path = gnome_vfs_volume_get_device_path (volume);
 	display_name = gnome_vfs_volume_get_display_name (volume);
-	hal_udi = gnome_vfs_volume_get_hal_udi (volume);
 	icon_name = gnome_vfs_volume_get_icon (volume);
 	rb_debug ("detecting new media - device_type=%d", device_type);
 	rb_debug ("detecting new media - volumd_type=%d", gnome_vfs_volume_get_volume_type (volume));
@@ -462,15 +543,17 @@
 	 * When volume is of the appropriate type, it creates a new source
 	 * to handle this volume
 	 */
-	if (source == NULL && rb_audiocd_is_volume_audiocd (volume))
-		source = rb_audiocd_source_new (shell, volume);
+	if (source == NULL)
+		source = rb_audiocd_source_new (priv->shell, volume, hal_udi);
 #ifdef WITH_IPOD_SUPPORT
-	if (source == NULL && rb_ipod_is_volume_ipod (volume))
-		source = rb_ipod_source_new (shell, volume);
+	if (source == NULL)
+		source = rb_ipod_source_new (priv->shell, volume, hal_udi);
 #endif
 
 	if (source) {
-		g_hash_table_insert (priv->volume_mapping, volume, source);
+		g_hash_table_insert (priv->udi_volume_mapping, g_strdup (hal_udi), volume);
+		g_hash_table_insert (priv->udi_source_mapping, g_strdup (hal_udi), source);
+		g_hash_table_insert (priv->volume_source_mapping, volume, source);
 		rb_removable_media_manager_append_media_source (mgr, source);
 	} else
 		rb_debug ("Unhanded media");
@@ -480,7 +563,6 @@
 	g_free (display_name);
 	g_free (hal_udi);
 	g_free (icon_name);
-	g_object_unref (G_OBJECT (shell));
 }
 
 static void
@@ -488,15 +570,21 @@
 {
 	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
 	RBRemovableMediaSource *source;
+	char *udi;
 
 	g_assert (volume != NULL);
 
 	rb_debug ("media removed");
-	source = g_hash_table_lookup (priv->volume_mapping, volume);
+	udi = gnome_vfs_volume_get_hal_udi (volume);
+	source = g_hash_table_lookup (priv->udi_source_mapping, udi);
 	if (source) {
 		rb_source_delete_thyself (RB_SOURCE (source));
-		g_hash_table_remove (priv->volume_mapping, volume);
+		g_hash_table_remove (priv->udi_volume_mapping, udi);
+		g_hash_table_remove (priv->udi_source_mapping, udi);
+		g_hash_table_remove (priv->volume_source_mapping, volume);
 	}
+
+	g_free (udi);
 }
 
 static void
@@ -621,7 +709,7 @@
 	check_data.volume_list = list;
 	check_data.manager = manager;
 	check_data.volumes_to_remove = NULL;
-	g_hash_table_foreach (priv->volume_mapping,
+	g_hash_table_foreach (priv->volume_source_mapping,
 			      (GHFunc) rb_removable_media_manager_check_volume,
 			      &check_data);
 	g_list_foreach (check_data.volumes_to_remove,
@@ -649,3 +737,48 @@
 	}
 	g_list_free (list);
 }
+
+static void
+rb_removable_media_manager_hal_device_added_cb (LibHalContext *ctx, const char *udi)
+{
+	RBRemovableMediaManager *mgr = libhal_ctx_get_user_data (ctx);
+	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
+	RBRemovableMediaSource *source = NULL;
+
+	rb_debug ("device %s added", udi);
+
+#ifdef WITH_NJB_SUPPORT
+	if (source == NULL)
+		source = rb_njb_source_new (priv->shell, NULL, udi);
+#endif
+
+	if (source) {
+		g_hash_table_insert (priv->udi_source_mapping, g_strdup (udi), source);
+		rb_removable_media_manager_append_media_source (mgr, source);
+	} else {
+		rb_debug ("unhandled device %s", udi);
+	}
+}
+
+static void
+rb_removable_media_manager_hal_device_removed_cb (LibHalContext *ctx, const char *udi)
+{
+	RBRemovableMediaManager *mgr = libhal_ctx_get_user_data (ctx);
+	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
+	RBRemovableMediaSource *source;
+
+	rb_debug ("device %s removed", udi);
+	
+	source = g_hash_table_lookup (priv->udi_source_mapping, udi);
+	if (source) {
+		GnomeVFSVolume *volume = g_hash_table_lookup (priv->udi_volume_mapping, udi);;
+
+		rb_source_delete_thyself (RB_SOURCE (source));
+		g_hash_table_remove (priv->udi_volume_mapping, udi);
+		g_hash_table_remove (priv->udi_source_mapping, udi);
+		g_hash_table_remove (priv->volume_source_mapping, volume);
+	}
+
+}
+
+
Index: sources/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/Makefile.am,v
retrieving revision 1.30
diff -u -u -r1.30 Makefile.am
--- sources/Makefile.am	10 Dec 2005 12:08:25 -0000	1.30
+++ sources/Makefile.am	16 Jan 2006 02:09:20 -0000
@@ -64,8 +64,16 @@
 libsourcesimpl_la_SOURCES +=         \
 	rb-ipod-source.c             \
 	rb-ipod-source.h
-INCLUDES += -I$(top_srcdir)/sources/ipod $(IPOD_CFLAGS)
+INCLUDES += $(IPOD_CFLAGS)
 endif
+
+if USE_NJB
+libsourcesimpl_la_SOURCES +=         \
+	rb-njb-source.c             \
+	rb-njb-source.h
+INCLUDES += $(NJB_CFLAGS)
+endif
+
 
 
 if USE_DAAP
Index: sources/rb-audiocd-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-audiocd-source.c,v
retrieving revision 1.17
diff -u -u -r1.17 rb-audiocd-source.c
--- sources/rb-audiocd-source.c	13 Jan 2006 12:20:01 -0000	1.17
+++ sources/rb-audiocd-source.c	16 Jan 2006 02:09:21 -0000
@@ -58,6 +58,7 @@
 static gboolean impl_show_popup (RBSource *source);
 static void impl_delete_thyself (RBSource *source);
 
+static gboolean rb_audiocd_is_volume_audiocd (GnomeVFSVolume *volume);
 static gboolean rb_audiocd_load_songs (RBAudioCdSource *source);
 static void rb_audiocd_load_metadata (RBAudioCdSource *source, RhythmDB *db);
 static void rb_audiocd_load_metadata_cancel (RBAudioCdSource *source);
@@ -144,13 +145,14 @@
 }
 
 RBRemovableMediaSource *
-rb_audiocd_source_new (RBShell *shell, GnomeVFSVolume *volume)
+rb_audiocd_source_new (RBShell *shell, GnomeVFSVolume *volume, const char *hal_udi)
 {
 	char *display_name, *device_path;
 	GObject *source;
 	RhythmDBEntryType entry_type;
 
-	g_assert (rb_audiocd_is_volume_audiocd (volume));
+	if (volume == NULL || !rb_audiocd_is_volume_audiocd (volume))
+		return NULL;
 
 	display_name = gnome_vfs_volume_get_display_name (volume);
 	device_path = gnome_vfs_volume_get_device_path (volume);
@@ -161,6 +163,7 @@
 			       "name", display_name,
 			       "entry-type", entry_type,
 			       "volume", volume,
+			       "hal-udi", hal_udi,
 			       "shell", shell,
 			       "sorting-key", NULL,
 			       NULL);
@@ -530,7 +533,7 @@
 }
 
 
-gboolean
+static gboolean
 rb_audiocd_is_volume_audiocd (GnomeVFSVolume *volume)
 {
 	char *device_path;
Index: sources/rb-audiocd-source.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-audiocd-source.h,v
retrieving revision 1.6
diff -u -u -r1.6 rb-audiocd-source.h
--- sources/rb-audiocd-source.h	3 Nov 2005 02:53:28 -0000	1.6
+++ sources/rb-audiocd-source.h	16 Jan 2006 02:09:21 -0000
@@ -45,10 +45,10 @@
 	RBRemovableMediaSourceClass parent;
 } RBAudioCdSourceClass;
 
-RBRemovableMediaSource *	rb_audiocd_source_new			(RBShell *shell, GnomeVFSVolume *volume);
+RBRemovableMediaSource *	rb_audiocd_source_new		(RBShell *shell,
+								 GnomeVFSVolume *volume,
+								 const char *hal_udi);
 GType			rb_audiocd_source_get_type		(void);
-
-gboolean		rb_audiocd_is_volume_audiocd		(GnomeVFSVolume *volume);
 
 G_END_DECLS
 
Index: sources/rb-ipod-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-ipod-source.c,v
retrieving revision 1.31
diff -u -u -r1.31 rb-ipod-source.c
--- sources/rb-ipod-source.c	13 Jan 2006 12:20:01 -0000	1.31
+++ sources/rb-ipod-source.c	16 Jan 2006 02:09:21 -0000
@@ -56,6 +56,9 @@
 static gchar *rb_ipod_get_mount_path (GnomeVFSVolume *volume);
 static void impl_delete_thyself (RBSource *source);
 
+static gboolean rb_ipod_is_volume_ipod (GnomeVFSVolume *volume);
+
+
 #ifdef HAVE_HAL
 static gboolean hal_udi_is_ipod (const char *udi);
 #endif
@@ -127,12 +130,13 @@
 }
 
 RBRemovableMediaSource *
-rb_ipod_source_new (RBShell *shell, GnomeVFSVolume *volume)
+rb_ipod_source_new (RBShell *shell, GnomeVFSVolume *volume, const char *hal_udi)
 {
 	RBiPodSource *source;
 	RhythmDBEntryType entry_type;
 
-	g_assert (rb_ipod_is_volume_ipod (volume));
+	if (volume == NULL || !rb_ipod_is_volume_ipod (volume))
+		return NULL;
 
 	entry_type =  rhythmdb_entry_register_type ();
 
@@ -140,6 +144,7 @@
 					  "name", _("iPod"),
 					  "entry-type", entry_type,
 					  "volume", volume,
+					  "hal-udi", hal_udi,
 					  "shell", shell,
 					  NULL));
 
@@ -441,7 +446,7 @@
 	return result;
 }
 
-gboolean
+static gboolean
 rb_ipod_is_volume_ipod (GnomeVFSVolume *volume)
 {
 	gchar *itunesdb_path;
Index: sources/rb-ipod-source.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-ipod-source.h,v
retrieving revision 1.4
diff -u -u -r1.4 rb-ipod-source.h
--- sources/rb-ipod-source.h	11 Sep 2005 03:05:11 -0000	1.4
+++ sources/rb-ipod-source.h	16 Jan 2006 02:09:21 -0000
@@ -45,10 +45,10 @@
 	RBRemovableMediaSourceClass parent;
 } RBiPodSourceClass;
 
-RBRemovableMediaSource *	rb_ipod_source_new		(RBShell *shell, GnomeVFSVolume *volume);
+RBRemovableMediaSource *	rb_ipod_source_new		(RBShell *shell,
+								 GnomeVFSVolume *volume,
+								 const char *hal_udi);
 GType			rb_ipod_source_get_type		(void);
-
-gboolean		rb_ipod_is_volume_ipod		(GnomeVFSVolume *volume);
 
 G_END_DECLS
 
Index: sources/rb-removable-media-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-removable-media-source.c,v
retrieving revision 1.7
diff -u -u -r1.7 rb-removable-media-source.c
--- sources/rb-removable-media-source.c	30 Nov 2005 22:18:28 -0000	1.7
+++ sources/rb-removable-media-source.c	16 Jan 2006 02:09:21 -0000
@@ -55,6 +55,7 @@
 typedef struct
 {
 	GnomeVFSVolume *volume;
+	char *hal_udi;
 } RBRemovableMediaSourcePrivate;
 
 G_DEFINE_TYPE (RBRemovableMediaSource, rb_removable_media_source, RB_TYPE_LIBRARY_SOURCE)
@@ -64,6 +65,7 @@
 {
 	PROP_0,
 	PROP_VOLUME,
+	PROP_HAL_UDI
 };
 
 
@@ -102,6 +104,13 @@
 							      "GnomeVfs Volume",
 							      GNOME_VFS_TYPE_VOLUME,
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (object_class,
+					 PROP_HAL_UDI,
+					 g_param_spec_string ("hal-udi",
+							      "hal-udi",
+							      "HAL UDI",
+							      NULL,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	g_type_class_add_private (klass, sizeof (RBRemovableMediaSourcePrivate));
 }
@@ -122,6 +131,11 @@
 		priv->volume = NULL;
 	}
 
+	if (priv->hal_udi) {
+		g_free (priv->hal_udi);
+		priv->hal_udi = NULL;
+	}
+
 	G_OBJECT_CLASS (rb_removable_media_source_parent_class)->dispose (object);
 }
 
@@ -140,7 +154,11 @@
 			gnome_vfs_volume_unref (priv->volume);
 		}
 		priv->volume = g_value_get_object (value);
-		gnome_vfs_volume_ref (priv->volume);
+		break;
+	case PROP_HAL_UDI:
+		if (priv->hal_udi)
+			g_free (priv->hal_udi);
+		priv->hal_udi = g_value_dup_string (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -162,6 +180,9 @@
 		gnome_vfs_volume_ref (priv->volume);
 		g_value_take_object (value, priv->volume);
 		break;
+	case PROP_HAL_UDI:
+		g_value_set_string (value, priv->hal_udi);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -172,13 +193,17 @@
 impl_get_pixbuf (RBSource *source)
 {
 	RBRemovableMediaSourcePrivate *priv = REMOVABLE_MEDIA_SOURCE_GET_PRIVATE (source);
+	GdkPixbuf *icon = NULL;
 	gint size;
 
-	char *icon_name = gnome_vfs_volume_get_icon (priv->volume);
-	GtkIconTheme *theme = gtk_icon_theme_get_default ();
-	gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
-	GdkPixbuf *icon = gtk_icon_theme_load_icon (theme, icon_name, size, 0, NULL);
-	g_free (icon_name);
+	if (priv->volume) {
+		char *icon_name = gnome_vfs_volume_get_icon (priv->volume);
+		GtkIconTheme *theme = gtk_icon_theme_get_default ();
+
+		gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
+		icon = gtk_icon_theme_load_icon (theme, icon_name, size, 0, NULL);
+		g_free (icon_name);
+	}
 
 	return icon;
 }
--- /dev/null	2006-01-16 10:46:00.955747680 +1100
+++ sources/rb-njb-source.h	2006-01-16 12:58:41.000000000 +1100
@@ -0,0 +1,55 @@
+/*
+ *  arch-tag: Header for NJB-based audio player source object
+ *
+ *  Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_NJB_SOURCE_H
+#define __RB_NJB_SOURCE_H
+
+#include "rb-shell.h"
+#include "rb-removable-media-source.h"
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_NJB_SOURCE         (rb_njb_source_get_type ())
+#define RB_NJB_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_NJB_SOURCE, RBNjbSource))
+#define RB_NJB_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_NJB_SOURCE, RBNjbSourceClass))
+#define RB_IS_NJB_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_NJB_SOURCE))
+#define RB_IS_NJB_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_NJB_SOURCE))
+#define RB_NJB_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_NJB_SOURCE, RBNjbSourceClass))
+
+typedef struct
+{
+	RBRemovableMediaSource parent;
+} RBNjbSource;
+
+typedef struct
+{
+	RBRemovableMediaSourceClass parent;
+} RBNjbSourceClass;
+
+RBRemovableMediaSource *	rb_njb_source_new		(RBShell *shell,
+								 GnomeVFSVolume *volume,
+								 const char *udi);
+GType				rb_njb_source_get_type		(void);
+
+G_END_DECLS
+
+#endif /* __RB_NJB_SOURCE_H */
--- /dev/null	2006-01-16 10:46:00.955747680 +1100
+++ sources/rb-njb-source.c	2006-01-16 12:58:41.000000000 +1100
@@ -0,0 +1,220 @@
+/*
+ *  arch-tag: Implementation of NJB-based audio player source object
+ *
+ *  Copyright (C) 2006 James Livingston  <jrl@ids.org.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <libgnome/gnome-i18n.h>
+#include <gtk/gtktreeview.h>
+#include <string.h>
+#include <libnjb.h>
+#include <libgnomevfs/gnome-vfs-volume.h>
+#include <libgnomevfs/gnome-vfs-volume-monitor.h>
+#ifdef HAVE_HAL_0_5
+#include <libhal.h>
+#include <dbus/dbus.h>
+#endif
+
+#include "rb-njb-source.h"
+#include "rhythmdb.h"
+#include "eel-gconf-extensions.h"
+#include "rb-debug.h"
+#include "rb-util.h"
+#include "rb-file-helpers.h"
+#include "rhythmdb.h"
+
+static GObject *rb_njb_source_constructor (GType type, guint n_construct_properties,
+						      GObjectConstructParam *construct_properties);
+static void rb_njb_source_dispose (GObject *object);
+
+static gboolean rb_njb_source_is_njb (const char *udi);
+static void rb_njb_source_load_songs (RBNjbSource *source);
+
+typedef struct
+{
+
+} RBNjbSourcePrivate;
+
+
+G_DEFINE_TYPE (RBNjbSource, rb_njb_source, RB_TYPE_REMOVABLE_MEDIA_SOURCE)
+#define NJB_SOURCE_GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_NJB_SOURCE, RBNjbSourcePrivate))
+
+
+static void
+rb_njb_source_class_init (RBNjbSourceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	/*RBSourceClass *source_class = RB_SOURCE_CLASS (klass);*/
+
+	object_class->constructor = rb_njb_source_constructor;
+	object_class->dispose = rb_njb_source_dispose;
+
+	g_type_class_add_private (klass, sizeof (RBNjbSourcePrivate));
+}
+
+static void
+rb_njb_source_init (RBNjbSource *source)
+{
+
+}
+
+static GObject *
+rb_njb_source_constructor (GType type, guint n_construct_properties,
+			       GObjectConstructParam *construct_properties)
+{
+	GObjectClass *klass, *parent_class; 
+	RBNjbSource *source; 
+
+	klass = G_OBJECT_CLASS (g_type_class_peek (type));
+	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+	source = RB_NJB_SOURCE (parent_class->constructor (type, n_construct_properties, construct_properties));
+
+	rb_njb_source_load_songs (source);
+
+	return G_OBJECT (source);
+}
+
+static void 
+rb_njb_source_dispose (GObject *object)
+{
+	/*RBNjbSourcePrivate *priv = NJB_SOURCE_GET_PRIVATE (object);*/
+
+	G_OBJECT_CLASS (rb_njb_source_parent_class)->dispose (object);
+}
+
+RBRemovableMediaSource *
+rb_njb_source_new (RBShell *shell, GnomeVFSVolume *volume, const char *hal_udi)
+{
+	RBNjbSource *source;
+	RhythmDBEntryType entry_type;
+
+	if (!rb_njb_source_is_njb (hal_udi))
+		return NULL;
+
+	entry_type =  rhythmdb_entry_register_type ();
+
+	source = RB_NJB_SOURCE (g_object_new (RB_TYPE_NJB_SOURCE,
+					  "entry-type", entry_type,
+					  "volume", NULL,
+					  "hal-udi", hal_udi,
+					  "shell", shell,
+					  NULL));
+
+	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
+
+	return RB_REMOVABLE_MEDIA_SOURCE (source);
+}
+
+static void
+rb_njb_source_load_songs (RBNjbSource *source)
+{
+	/*RBNjbSourcePrivate *priv = NJB_SOURCE_GET_PRIVATE (source);*/
+	RBShell *shell;
+	RhythmDB *db;
+	RhythmDBEntryType entry_type;
+
+	g_object_get (G_OBJECT (source), "entry-type", &entry_type, NULL);
+	g_object_get (G_OBJECT (source), "shell", &shell, NULL);
+	g_object_get (G_OBJECT (shell), "db", &db, NULL);
+	g_object_unref (G_OBJECT (shell));
+
+	/*db, entry_type*/
+}
+
+static gboolean
+rb_njb_source_is_njb (const char *udi)
+{
+	gboolean result = FALSE;
+#ifdef HAVE_HAL_0_5
+
+	if (udi != NULL) {
+		LibHalContext *ctx = NULL;
+		DBusConnection *conn = NULL;
+		DBusError error;
+		char *prop = NULL;
+		njb_t *njb_device = NULL;
+		gulong device_id, bus_id;
+
+		dbus_error_init (&error);
+		ctx = libhal_ctx_new ();
+		if (ctx == NULL) {
+			g_print ("Error: %s\n", error.message);
+			goto end_hal;
+		}
+		conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+		if (conn == NULL) {
+			g_print ("Error: %s\n", error.message);
+			goto end_hal;
+		}
+		libhal_ctx_set_dbus_connection (ctx, conn);
+		if (!libhal_ctx_init (ctx, &error)) {
+			g_print ("Error: %s\n", error.message);
+			goto end_hal;
+		}
+
+
+		bus_id = libhal_device_get_property_int (ctx, udi, "usb.bus_number", &error);
+		device_id = libhal_device_get_property_int (ctx, udi, "usb.linux.device_number", &error);
+
+		{
+			njb_t njbs[NJB_MAX_DEVICES];
+			int njb_n, i;
+
+			NJB_Discover (njbs, NJB_MAX_DEVICES, &njb_n);
+			for (i = 0; i < njb_n; i++) {
+				gulong njb_device_id, njb_bus_id;
+
+				NJB_Open (&njbs[i]);
+
+				njb_device_id = njbs[i].device->devnum;
+				njb_bus_id = njbs[i].device->bus->location;
+				rb_debug ("Checking NJB device %d:%d", njb_device_id, njb_bus_id);
+				/* determine whether this is the correct device*/
+				if ((njb_device_id == device_id) && (njb_bus_id == bus_id)) {
+					njb_device = &njbs[i];
+					break;
+				}
+				
+				NJB_Close (&njbs[i]);
+			}
+
+			if (njb_device) {
+				rb_debug ("Found correct NJB device");
+				result = TRUE;
+				NJB_Close (njb_device);
+			} 
+		}
+
+end_hal:
+		if (prop)
+			libhal_free_string (prop);
+
+		if (ctx) {
+			libhal_ctx_shutdown (ctx, &error);
+			libhal_ctx_free (ctx);
+		}
+		dbus_error_free (&error);
+
+	}
+#endif /* HAVE_HAL_0_5 */
+
+	return result;
+}
+
