diff --git a/data/glade/library-prefs.glade b/data/glade/library-prefs.glade
index 082d43b..22d5cce 100644
--- a/data/glade/library-prefs.glade
+++ b/data/glade/library-prefs.glade
@@ -107,124 +107,94 @@
 		      <property name="spacing">3</property>
 
 		      <child>
-			<widget class="GtkHBox" id="hbox15">
+			<widget class="GtkLabel" id="label25">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">_Music files are placed in:</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkHBox" id="hbox20">
 			  <property name="visible">True</property>
 			  <property name="homogeneous">False</property>
 			  <property name="spacing">6</property>
 
 			  <child>
-			    <widget class="GtkLabel" id="label25">
-			      <property name="visible">True</property>
-			      <property name="label" translatable="yes">_Music files are placed in:</property>
-			      <property name="use_underline">True</property>
-			      <property name="use_markup">False</property>
-			      <property name="justify">GTK_JUSTIFY_LEFT</property>
-			      <property name="wrap">False</property>
-			      <property name="selectable">False</property>
-			      <property name="xalign">0.5</property>
-			      <property name="yalign">0.5</property>
-			      <property name="xpad">0</property>
-			      <property name="ypad">0</property>
-			      <property name="mnemonic_widget">library_location_entry</property>
-			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-			      <property name="width_chars">-1</property>
-			      <property name="single_line_mode">False</property>
-			      <property name="angle">0</property>
-			    </widget>
-			    <packing>
-			      <property name="padding">0</property>
-			      <property name="expand">False</property>
-			      <property name="fill">False</property>
-			    </packing>
-			  </child>
-
-			  <child>
-			    <widget class="GtkEntry" id="library_location_entry">
+			    <widget class="GtkScrolledWindow" id="scrolledwindow1">
 			      <property name="visible">True</property>
 			      <property name="can_focus">True</property>
-			      <property name="editable">True</property>
-			      <property name="visibility">True</property>
-			      <property name="max_length">0</property>
-			      <property name="text"></property>
-			      <property name="has_frame">True</property>
-			      <property name="invisible_char">*</property>
-			      <property name="activates_default">False</property>
+			      <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+			      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			      <property name="shadow_type">GTK_SHADOW_IN</property>
+			      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+			      <child>
+				<widget class="GtkTreeView" id="library_locations_treeview">
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="headers_visible">True</property>
+				  <property name="rules_hint">False</property>
+				  <property name="reorderable">False</property>
+				  <property name="enable_search">False</property>
+				  <property name="fixed_height_mode">False</property>
+				  <property name="hover_selection">False</property>
+				  <property name="hover_expand">False</property>
+				  <property name="height_request">100</property>
+				</widget>
+			      </child>
 			    </widget>
 			    <packing>
 			      <property name="padding">0</property>
-			      <property name="expand">False</property>
-			      <property name="fill">False</property>
+			      <property name="expand">True</property>
+			      <property name="fill">True</property>
 			    </packing>
 			  </child>
 
 			  <child>
-			    <widget class="GtkButton" id="library_location_button">
+			    <widget class="GtkVButtonBox" id="vbuttonbox1">
 			      <property name="visible">True</property>
-			      <property name="can_focus">True</property>
-			      <property name="relief">GTK_RELIEF_NORMAL</property>
-			      <property name="focus_on_click">True</property>
+			      <property name="layout_style">GTK_BUTTONBOX_START</property>
+			      <property name="spacing">6</property>
 
 			      <child>
-				<widget class="GtkAlignment" id="alignment3">
+				<widget class="GtkButton" id="add_library_location_button">
 				  <property name="visible">True</property>
-				  <property name="xalign">0.5</property>
-				  <property name="yalign">0.5</property>
-				  <property name="xscale">0</property>
-				  <property name="yscale">0</property>
-				  <property name="top_padding">0</property>
-				  <property name="bottom_padding">0</property>
-				  <property name="left_padding">0</property>
-				  <property name="right_padding">0</property>
-
-				  <child>
-				    <widget class="GtkHBox" id="hbox16">
-				      <property name="visible">True</property>
-				      <property name="homogeneous">False</property>
-				      <property name="spacing">2</property>
-
-				      <child>
-					<widget class="GtkImage" id="image1">
-					  <property name="visible">True</property>
-					  <property name="stock">gtk-directory</property>
-					  <property name="icon_size">4</property>
-					  <property name="xalign">0.5</property>
-					  <property name="yalign">0.5</property>
-					  <property name="xpad">0</property>
-					  <property name="ypad">0</property>
-					</widget>
-					<packing>
-					  <property name="padding">0</property>
-					  <property name="expand">False</property>
-					  <property name="fill">False</property>
-					</packing>
-				      </child>
+				  <property name="can_default">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="label">gtk-add</property>
+				  <property name="use_stock">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+				</widget>
+			      </child>
 
-				      <child>
-					<widget class="GtkLabel" id="label21">
-					  <property name="visible">True</property>
-					  <property name="label" translatable="yes">_Browse...</property>
-					  <property name="use_underline">True</property>
-					  <property name="use_markup">False</property>
-					  <property name="justify">GTK_JUSTIFY_LEFT</property>
-					  <property name="wrap">False</property>
-					  <property name="selectable">False</property>
-					  <property name="xalign">0.5</property>
-					  <property name="yalign">0.5</property>
-					  <property name="xpad">0</property>
-					  <property name="ypad">0</property>
-					  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-					  <property name="width_chars">-1</property>
-					  <property name="single_line_mode">False</property>
-					  <property name="angle">0</property>
-					</widget>
-					<packing>
-					  <property name="padding">0</property>
-					  <property name="expand">False</property>
-					  <property name="fill">False</property>
-					</packing>
-				      </child>
-				    </widget>
-				  </child>
+			      <child>
+				<widget class="GtkButton" id="remove_library_location_button">
+				  <property name="visible">True</property>
+				  <property name="can_default">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="label">gtk-remove</property>
+				  <property name="use_stock">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
 				</widget>
 			      </child>
 			    </widget>
@@ -237,27 +207,8 @@
 			</widget>
 			<packing>
 			  <property name="padding">0</property>
-			  <property name="expand">False</property>
-			  <property name="fill">False</property>
-			</packing>
-		      </child>
-
-		      <child>
-			<widget class="GtkCheckButton" id="watch_library_check">
-			  <property name="visible">True</property>
-			  <property name="can_focus">True</property>
-			  <property name="label" translatable="yes">_Watch my library for new files</property>
-			  <property name="use_underline">True</property>
-			  <property name="relief">GTK_RELIEF_NORMAL</property>
-			  <property name="focus_on_click">True</property>
-			  <property name="active">False</property>
-			  <property name="inconsistent">False</property>
-			  <property name="draw_indicator">True</property>
-			</widget>
-			<packing>
-			  <property name="padding">0</property>
-			  <property name="expand">False</property>
-			  <property name="fill">False</property>
+			  <property name="expand">True</property>
+			  <property name="fill">True</property>
 			</packing>
 		      </child>
 		    </widget>
diff --git a/data/rhythmbox.schemas b/data/rhythmbox.schemas
index 89f0812..5cad0a6 100644
--- a/data/rhythmbox.schemas
+++ b/data/rhythmbox.schemas
@@ -45,6 +45,17 @@
         </locale>
       </schema>
       <schema>
+        <key>/schemas/apps/rhythmbox/default_library_location</key>
+        <applyto>/apps/rhythmbox/default_library_location</applyto>
+        <owner>rhythmbox</owner>
+        <type>string</type>
+        <default></default>
+        <locale name="C">
+        <short>Directory where Rhyhtmbox should put files</short>
+        <long>The URI of the directory where Rhythmbox puts files from e.g. ripped CDs or tracks transferred from portable media players</long>
+        </locale>
+      </schema>
+      <schema>
         <key>/schemas/apps/rhythmbox/library_locations</key>
         <applyto>/apps/rhythmbox/library_locations</applyto>
         <owner>rhythmbox</owner>
@@ -101,14 +112,15 @@
 	</locale>
       </schema>
       <schema>
-        <key>/schemas/apps/rhythmbox/monitor_library</key>
-        <applyto>/apps/rhythmbox/monitor_library</applyto>
+        <key>/schemas/apps/rhythmbox/monitor_library_locations</key>
+        <applyto>/apps/rhythmbox/monitor_library_locations</applyto>
         <owner>rhythmbox</owner>
-        <type>bool</type>
-        <default>FALSE</default>
+        <type>list</type>
+        <list_type>string</list_type>
+        <default>[]</default>
         <locale name="C">
-          <short>Whether the library location are monitored</short>
-          <long>If true, the locations listed in /apps/rhythmbox/library_locations are monitored for new files</long>
+          <short>Library locations which are monitored for new tracks</short>
+          <long>A list of directory URIs Rhythmbox monitors for new tracks. This is a subset of the list in library_locations.</long>
         </locale>
       </schema>
       <schema>
diff --git a/lib/eel-gconf-extensions.c b/lib/eel-gconf-extensions.c
index 00f469d..4a08d16 100644
--- a/lib/eel-gconf-extensions.c
+++ b/lib/eel-gconf-extensions.c
@@ -348,6 +348,26 @@ eel_gconf_unset (const char *key)
         eel_gconf_handle_error (&error);
 }
 
+/* This code wasn't part of the original eel-gconf-extensions.c */
+void
+eel_gconf_recursive_unset (const char *key)
+{
+	GConfClient *client;
+	GError *error = NULL;
+
+	g_return_if_fail (key != NULL);
+
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_recursive_unset (client,
+			key,
+			GCONF_UNSET_INCLUDING_SCHEMA_NAMES,
+			&error);
+	eel_gconf_handle_error (&error);
+}
+/* End of added code */
+
 gboolean
 eel_gconf_monitor_add (const char *directory)
 {
diff --git a/lib/eel-gconf-extensions.h b/lib/eel-gconf-extensions.h
index 1ac7fae..b87382c 100644
--- a/lib/eel-gconf-extensions.h
+++ b/lib/eel-gconf-extensions.h
@@ -72,6 +72,7 @@ guint        eel_gconf_notification_add    (const char *key,
 void         eel_gconf_notification_remove (guint notification_id);
 
 void	     eel_gconf_unset 		   (const char *key);
+void	eel_gconf_recursive_unset	(const char *key);
 
 #ifdef __cplusplus
 }
diff --git a/lib/rb-preferences.h b/lib/rb-preferences.h
index 77a5c53..607a308 100644
--- a/lib/rb-preferences.h
+++ b/lib/rb-preferences.h
@@ -66,8 +66,10 @@ G_BEGIN_DECLS
 #define CONF_DAAP_SHARE_PASSWORD   CONF_PREFIX "/sharing/share_password"
 #define CONF_DAAP_REQUIRE_PASSWORD CONF_PREFIX "/sharing/require_password"
 
-#define CONF_LIBRARY_LOCATION	CONF_PREFIX "/library_locations"
-#define CONF_MONITOR_LIBRARY	CONF_PREFIX "/monitor_library"
+#define CONF_LIBRARY_LOCATION		CONF_PREFIX "/library_locations"
+#define CONF_MONITOR_LIBRARY		CONF_PREFIX "/monitor_library" /* obsolete, only for backward compatibility */
+#define CONF_DEFAULT_LIBRARY_LOCATION	CONF_PREFIX "/default_library_location"
+#define CONF_MONITOR_LIBRARY_LOCATIONS	CONF_PREFIX "/monitor_library_locations"
 #define CONF_LIBRARY_STRIP_CHARS	CONF_PREFIX "/library_strip_chars"
 #define CONF_LIBRARY_LAYOUT_PATH	CONF_PREFIX "/library_layout_path"
 #define CONF_LIBRARY_LAYOUT_FILENAME	CONF_PREFIX "/library_layout_filename"
diff --git a/lib/rb-util.c b/lib/rb-util.c
index 30e6003..7cd8a4d 100644
--- a/lib/rb-util.c
+++ b/lib/rb-util.c
@@ -695,6 +695,26 @@ rb_string_list_copy (GList *list)
 	return copy;
 }
 
+static void
+slist_copy_cb (const char *s, GSList **list)
+{
+	*list = g_slist_prepend (*list, g_strdup (s));
+}
+
+GSList *
+rb_string_slist_copy (GSList *list)
+{
+	GSList *copy = NULL;
+
+	if (list == NULL)
+		return NULL;
+
+	g_slist_foreach (list, (GFunc)slist_copy_cb, &copy);
+	copy = g_slist_reverse (copy);
+
+	return copy;
+}
+
 gboolean
 rb_string_list_contains (GList *list, const char *s)
 {
@@ -708,6 +728,19 @@ rb_string_list_contains (GList *list, const char *s)
 	return FALSE;
 }
 
+gboolean
+rb_string_slist_contains (GSList *list, const char *s)
+{
+	GSList *l;
+
+	for (l = list; l != NULL; l = g_slist_next (l)) {
+		if (strcmp ((const char *)l->data, s) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
 void
 rb_list_destroy_free (GList *list, GDestroyNotify destroyer)
 {
diff --git a/lib/rb-util.h b/lib/rb-util.h
index f96d3e3..c8bd0a5 100644
--- a/lib/rb-util.h
+++ b/lib/rb-util.h
@@ -64,7 +64,9 @@ gchar** rb_string_split_words (const gchar *string);
 
 gboolean rb_string_list_equal (GList *a, GList *b);
 gboolean rb_string_list_contains (GList *list, const char *s);
+gboolean rb_string_slist_contains (GSList *list, const char *s);
 GList* rb_string_list_copy (GList *list);
+GSList* rb_string_slist_copy (GSList *list);
 
 void rb_list_deep_free (GList *list);
 void rb_list_destroy_free (GList *list, GDestroyNotify destroyer);
diff --git a/rhythmdb/rhythmdb-monitor.c b/rhythmdb/rhythmdb-monitor.c
index 602816d..079851c 100644
--- a/rhythmdb/rhythmdb-monitor.c
+++ b/rhythmdb/rhythmdb-monitor.c
@@ -159,8 +159,8 @@ monitor_entry_file (RhythmDBEntry *entry, RhythmDB *db)
 
 		loc = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 
-		/* don't add add monitor if it's in the library path */
-		for (l = db->priv->library_locations; l != NULL; l = g_slist_next (l)) {
+		/* don't add a monitor if it's in the library path */
+		for (l = db->priv->monitored_locations; l != NULL; l = g_slist_next (l)) {
 			if (g_str_has_prefix (loc, (const char*)l->data))
 				return;
 				
@@ -264,8 +264,8 @@ rhythmdb_start_monitoring (RhythmDB *db)
 	g_thread_create ((GThreadFunc)_monitor_entry_thread, g_object_ref (db), FALSE, NULL);
 
 	/* monitor all library locations */
-	if (db->priv->library_locations)
-		g_slist_foreach (db->priv->library_locations, (GFunc) monitor_library_directory, db);
+	if (db->priv->monitored_locations)
+		g_slist_foreach (db->priv->monitored_locations, (GFunc) monitor_library_directory, db);
 }
 
 static void
@@ -300,23 +300,20 @@ rhythmdb_directory_change_cb (GFileMonitor *monitor,
         case G_FILE_MONITOR_EVENT_CREATED:
 		{
 			GSList *cur;
-			gboolean in_library = FALSE;
-
-			if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY))
-				break;
+			gboolean in_monitored_location = FALSE;
 
 			if (rb_uri_is_hidden (canon_uri))
 				break;
 
-			/* ignore new files outside of the library locations */
-			for (cur = db->priv->library_locations; cur != NULL; cur = g_slist_next (cur)) {
+			/* ignore new files outside of a monitored location */
+			for (cur = db->priv->monitored_locations; cur != NULL; cur = g_slist_next (cur)) {
 				if (g_str_has_prefix (canon_uri, cur->data)) {
-					in_library = TRUE;
+					in_monitored_location = TRUE;
 					break;
 				}
 			}
 
-			if (!in_library)
+			if (!in_monitored_location)
 				break;
 		}
 
diff --git a/rhythmdb/rhythmdb-private.h b/rhythmdb/rhythmdb-private.h
index aa125f7..a170d10 100644
--- a/rhythmdb/rhythmdb-private.h
+++ b/rhythmdb/rhythmdb-private.h
@@ -156,9 +156,8 @@ struct _RhythmDBPrivate
 	GVolumeMonitor *volume_monitor;
 	GHashTable *monitored_directories;
 	GHashTable *changed_files;
-	guint library_location_notify_id;
 	guint changed_files_id;
-	GSList *library_locations;
+	GSList *monitored_locations;
 	guint monitor_notify_id;
 	GMutex *monitor_mutex;
 
diff --git a/rhythmdb/rhythmdb.c b/rhythmdb/rhythmdb.c
index 64f5b91..11536f6 100644
--- a/rhythmdb/rhythmdb.c
+++ b/rhythmdb/rhythmdb.c
@@ -136,11 +136,7 @@ static void rhythmdb_entry_set_mount_point (RhythmDB *db,
  					    const gchar *realuri);
 
 static gboolean rhythmdb_idle_save (RhythmDB *db);
-static void library_location_changed_cb (GConfClient *client,
-					  guint cnxn_id,
-					  GConfEntry *entry,
-					  RhythmDB *db);
-static void rhythmdb_sync_library_location (RhythmDB *db);
+static void rhythmdb_sync_monitored_locations (RhythmDB *db);
 static void rhythmdb_entry_sync_mirrored (RhythmDBEntry *entry,
 					  guint propid);
 static void rhythmdb_register_core_entry_types (RhythmDB *db);
@@ -703,7 +699,7 @@ rhythmdb_init (RhythmDB *db)
 	rhythmdb_init_monitoring (db);
 
 	db->priv->monitor_notify_id = 
-		eel_gconf_notification_add (CONF_MONITOR_LIBRARY,
+		eel_gconf_notification_add (CONF_MONITOR_LIBRARY_LOCATIONS,
 					   (GConfClientNotifyFunc)rhythmdb_monitor_library_changed_cb,
 					   db);
 }
@@ -964,14 +960,10 @@ rhythmdb_shutdown (RhythmDB *db)
 	action->type = RHYTHMDB_ACTION_QUIT;
 	g_async_queue_push (db->priv->action_queue, action);
 
-	eel_gconf_notification_remove (db->priv->library_location_notify_id);
-	db->priv->library_location_notify_id = 0;
-	g_slist_foreach (db->priv->library_locations, (GFunc) g_free, NULL);
-	g_slist_free (db->priv->library_locations);
-	db->priv->library_locations = NULL;
-
 	eel_gconf_notification_remove (db->priv->monitor_notify_id);
 	db->priv->monitor_notify_id = 0;
+	rb_slist_deep_free (db->priv->monitored_locations);
+	db->priv->monitored_locations = NULL;
 
 	/* abort all async io operations */
 	g_mutex_lock (db->priv->stat_mutex);
@@ -2338,8 +2330,11 @@ rhythmdb_process_metadata_load_real (RhythmDBEvent *event)
 
 	/* monitor the file for changes */
 	/* FIXME: watch for errors */
-	if (eel_gconf_get_boolean (CONF_MONITOR_LIBRARY) && event->entry_type == RHYTHMDB_ENTRY_TYPE_SONG)
+	/* rhyhtmdb_monitor_uri_path calls actually_add_monitor, which checks
+	 * if entry->location is in one of the monitored dirs */
+	if (event->entry_type == RHYTHMDB_ENTRY_TYPE_SONG) {
 		rhythmdb_monitor_uri_path (event->db, rb_refstring_get (entry->location), NULL);
+	}
 
 	rhythmdb_add_timeout_commit (event->db, FALSE);
 
@@ -3073,7 +3068,7 @@ rhythmdb_add_uri_with_types (RhythmDB *db,
 static gboolean
 rhythmdb_sync_library_idle (RhythmDB *db)
 {
-	rhythmdb_sync_library_location (db);
+	rhythmdb_sync_monitored_locations (db);
 	g_object_unref (db);
 	return FALSE;
 }
@@ -4845,33 +4840,27 @@ rhythmdb_idle_save (RhythmDB *db)
 }
 
 static void
-rhythmdb_sync_library_location (RhythmDB *db)
+rhythmdb_sync_monitored_locations (RhythmDB *db)
 {
-	gboolean reload = (db->priv->library_locations != NULL);
-
-	if (db->priv->library_location_notify_id == 0) {
-		db->priv->library_location_notify_id =
-			eel_gconf_notification_add (CONF_LIBRARY_LOCATION,
-						    (GConfClientNotifyFunc) library_location_changed_cb,
-						    db);
-	}
+	GSList *list;
 
-	if (reload) {
+	if (db->priv->monitored_locations != NULL) {
 		rb_debug ("ending monitor of old library directories");
 
 		rhythmdb_stop_monitoring (db);
 
-		g_slist_foreach (db->priv->library_locations, (GFunc) g_free, NULL);
-		g_slist_free (db->priv->library_locations);
-		db->priv->library_locations = NULL;
+		rb_slist_deep_free (db->priv->monitored_locations);
+		db->priv->monitored_locations = NULL;
 	}
 
-	if (eel_gconf_get_boolean (CONF_MONITOR_LIBRARY)) {
+	list = eel_gconf_get_string_list (CONF_MONITOR_LIBRARY_LOCATIONS);
+	if (g_slist_length (list) > 0) {
 		rb_debug ("starting library monitoring");
-		db->priv->library_locations = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+		db->priv->monitored_locations = rb_string_slist_copy (list);
 
 		rhythmdb_start_monitoring (db);
 	}
+	rb_slist_deep_free (list);
 }
 
 static
@@ -4880,17 +4869,8 @@ void rhythmdb_monitor_library_changed_cb (GConfClient *client,
 					  GConfEntry *entry,
 					  RhythmDB *db)
 {
-	rb_debug ("'watch library' key changed");
-	rhythmdb_sync_library_location (db);
-}
-
-static void
-library_location_changed_cb (GConfClient *client,
-			     guint cnxn_id,
-			     GConfEntry *entry,
-			     RhythmDB *db)
-{
-	rhythmdb_sync_library_location (db);
+	rb_debug ("'monitor_locations' key changed");
+	rhythmdb_sync_monitored_locations (db);
 }
 
 char *
diff --git a/sources/Makefile.am b/sources/Makefile.am
index 9dce587..0e1cb67 100644
--- a/sources/Makefile.am
+++ b/sources/Makefile.am
@@ -20,6 +20,10 @@ libsourcesimpl_la_SOURCES =		\
 	rb-sourcelist-model.h		\
 	rb-browser-source.c		\
 	rb-browser-source.h		\
+	rb-library-file-helpers.c	\
+	rb-library-file-helpers.h	\
+	rb-library-preferences.c	\
+	rb-library-preferences.h	\
 	rb-library-source.c		\
 	rb-library-source.h		\
 	rb-podcast-source.c		\
@@ -41,6 +45,8 @@ libsourcesimpl_la_SOURCES =		\
 	rb-import-errors-source.h	\
 	rb-source-search-basic.c	\
 	rb-source-search-basic.h	\
+	rb-library-child-source.c	\
+	rb-library-child-source.h	\
 	$(NULL)
 
 INCLUDES =						\
diff --git a/sources/rb-browser-source.c b/sources/rb-browser-source.c
index 52c8039..246574c 100644
--- a/sources/rb-browser-source.c
+++ b/sources/rb-browser-source.c
@@ -136,6 +136,7 @@ struct RBBrowserSourcePrivate
 	RBEntryView *songs;
 	GtkWidget *paned;
 
+	GPtrArray *base_query;
 	RhythmDBQueryModel *cached_all_query;
 	RhythmDBQuery *search_query;
 	RhythmDBPropType search_prop;
@@ -189,7 +190,8 @@ enum
 	PROP_SORTING_KEY,
 	PROP_BASE_QUERY_MODEL,
 	PROP_POPULATE,
-	PROP_SEARCH_TYPE
+	PROP_SEARCH_TYPE,
+	PROP_QUERY
 };
 
 G_DEFINE_ABSTRACT_TYPE (RBBrowserSource, rb_browser_source, RB_TYPE_SOURCE)
@@ -239,7 +241,7 @@ rb_browser_source_class_init (RBBrowserSourceClass *klass)
 	g_object_class_override_property (object_class,
 					  PROP_BASE_QUERY_MODEL,
 					  "base-query-model");
-	
+
 	g_object_class_install_property (object_class,
 					 PROP_POPULATE,
 					 g_param_spec_boolean ("populate",
@@ -252,6 +254,13 @@ rb_browser_source_class_init (RBBrowserSourceClass *klass)
 					  PROP_SEARCH_TYPE,
 					  "search-type");
 
+	g_object_class_install_property (object_class,
+					 PROP_QUERY,
+					 g_param_spec_pointer ("query",
+							      "Query",
+							      "RhythmDBQuery",
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
 	g_type_class_add_private (klass, sizeof (RBBrowserSourcePrivate));
 }
 
@@ -284,6 +293,11 @@ rb_browser_source_dispose (GObject *object)
 		source->priv->search_query = NULL;
 	}
 
+	if (source->priv->base_query != NULL) {
+		rhythmdb_query_free (source->priv->base_query);
+		source->priv->base_query = NULL;
+	}
+
 	if (source->priv->cached_all_query != NULL) {
 		g_object_unref (source->priv->cached_all_query);
 		source->priv->cached_all_query = NULL;
@@ -517,6 +531,12 @@ rb_browser_source_set_property (GObject *object,
 	case PROP_SEARCH_TYPE:
 		/* ignored */
 		break;
+	case PROP_QUERY:
+		if (source->priv->base_query != NULL) {
+			rhythmdb_query_free (source->priv->base_query);
+		}
+		source->priv->base_query = rhythmdb_query_copy (g_value_get_pointer (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -544,6 +564,9 @@ rb_browser_source_get_property (GObject *object,
 	case PROP_SEARCH_TYPE:
 		g_value_set_enum (value, RB_SOURCE_SEARCH_INCREMENTAL);
 		break;
+	case PROP_QUERY:
+		source->priv->base_query = rhythmdb_query_copy (g_value_get_pointer (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -575,11 +598,17 @@ rb_browser_source_populate (RBBrowserSource *source)
 				 source, 0);
 
 	g_object_get (source, "entry-type", &entry_type, NULL);
-	rhythmdb_do_full_query_async (source->priv->db,
-				      RHYTHMDB_QUERY_RESULTS (source->priv->cached_all_query),
-				      RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, entry_type,
-				      RHYTHMDB_QUERY_END);
+	if (source->priv->base_query == NULL) {
+		rb_debug ("No query set, creating new one.");
+		source->priv->base_query = rhythmdb_query_parse (source->priv->db,
+				RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, entry_type,
+				RHYTHMDB_QUERY_END);
+	}
 	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+
+	rhythmdb_do_full_query_async_parsed (source->priv->db,
+				      RHYTHMDB_QUERY_RESULTS (source->priv->cached_all_query),
+				      source->priv->base_query);
 }
 
 
@@ -908,8 +937,8 @@ static void
 rb_browser_source_do_query (RBBrowserSource *source, gboolean subset)
 {
 	RhythmDBQueryModel *query_model;
+	GPtrArray *base_query;
 	GPtrArray *query;
-	RhythmDBEntryType entry_type;
 
 	/* use the cached 'all' query to optimise the no-search case */
 	if (source->priv->search_query == NULL) {
@@ -919,15 +948,13 @@ rb_browser_source_do_query (RBBrowserSource *source, gboolean subset)
 		return;
 	}
 
-	g_object_get (source, "entry-type", &entry_type, NULL);
-	query = rhythmdb_query_parse (source->priv->db,
-				      RHYTHMDB_QUERY_PROP_EQUALS,
-				      RHYTHMDB_PROP_TYPE,
-				      entry_type,
-				      RHYTHMDB_QUERY_SUBQUERY,
-				      source->priv->search_query,
-				      RHYTHMDB_QUERY_END);
-	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+	g_object_get (source->priv->cached_all_query, "query", &base_query, NULL);
+	query = rhythmdb_query_copy (base_query);
+	rhythmdb_query_append (source->priv->db,
+				query,
+				RHYTHMDB_QUERY_SUBQUERY,
+				source->priv->search_query,
+				RHYTHMDB_QUERY_END);
 
 	if (subset) {
 		/* if we're appending text to an existing search string, the results will be a subset
diff --git a/sources/rb-library-child-source.c b/sources/rb-library-child-source.c
new file mode 100644
index 0000000..b3933ed
--- /dev/null
+++ b/sources/rb-library-child-source.c
@@ -0,0 +1,392 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2008 Christian Becke <christianbecke@gmail.com>
+ *
+ *  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.
+ *
+ *  The Rhythmbox authors hereby grants permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gconf/gconf.h>
+
+#include "rhythmdb.h"
+#include "rb-debug.h"
+#include "rb-preferences.h"
+#include "rb-browser-source.h"
+#include "rb-library-child-source.h"
+#include "eel-gconf-extensions.h"
+
+static void rb_library_child_source_class_init (RBLibraryChildSourceClass *klass);
+static void rb_library_child_source_init (RBLibraryChildSource *source);
+static void rb_library_child_source_finalize (GObject *object);
+static void rb_library_child_source_set_property (GObject *object,
+							guint prop_id,
+							const GValue *value,
+							GParamSpec *pspec);
+static void rb_library_child_source_get_property (GObject *object,
+							guint prop_id,
+							GValue *value,
+							GParamSpec *pspec);
+static void impl_delete_thyself (RBSource *source);
+
+static char *get_state_dir (const char *path);
+static gboolean delete_entry (GtkTreeModel *model,
+				GtkTreePath *path,
+				GtkTreeIter *iter,
+				RhythmDB *db);
+static char *get_display_name_for_uri (const char *uri);
+
+/* RBSource implementations */
+static char *impl_get_browser_key (RBSource *source);
+static char *impl_get_paned_key (RBBrowserSource *source);
+
+#define CONF_STATE_LIBRARY_DIR CONF_PREFIX "/state/library" /* Move this one to rb-preferences.h? */
+#define CONF_STATE_LIBRARY_CHILD_SORTING "/sorting"
+#define CONF_STATE_LIBRARY_CHILD_PANED_POSITION "/paned_position"
+#define CONF_STATE_LIBRARY_CHILD_SHOW_BROWSER "/show_browser"
+
+struct _RBLibraryChildSourcePrivate
+{
+	char *path;
+
+	char *browser_key;
+	char *paned_key;
+
+	gboolean keep_songs_and_state;
+};
+
+enum
+{
+	PROP_0,
+	PROP_PATH
+};
+
+#define RB_LIBRARY_CHILD_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_LIBRARY_CHILD_SOURCE, RBLibraryChildSourcePrivate))
+G_DEFINE_TYPE (RBLibraryChildSource, rb_library_child_source, RB_TYPE_BROWSER_SOURCE);
+
+static void
+rb_library_child_source_class_init (RBLibraryChildSourceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+	RBBrowserSourceClass *browser_source_class = RB_BROWSER_SOURCE_CLASS (klass);
+
+	object_class->finalize = rb_library_child_source_finalize;
+	object_class->get_property = rb_library_child_source_get_property;
+	object_class->set_property = rb_library_child_source_set_property;
+
+	source_class->impl_get_browser_key = impl_get_browser_key;
+	source_class->impl_delete_thyself = impl_delete_thyself;
+
+	browser_source_class->impl_get_paned_key = impl_get_paned_key;
+
+	g_object_class_install_property (object_class,
+					PROP_PATH,
+					g_param_spec_string ("path",
+							"path",
+							"Path to library",
+							NULL,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_type_class_add_private (klass, sizeof (RBLibraryChildSourcePrivate));
+}
+
+static void
+rb_library_child_source_init (RBLibraryChildSource *source)
+{
+	source->priv = RB_LIBRARY_CHILD_SOURCE_GET_PRIVATE (source);
+
+	source->priv->keep_songs_and_state = FALSE;
+}
+
+static void
+rb_library_child_source_finalize (GObject *object)
+{
+	RBLibraryChildSource *source;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (RB_IS_LIBRARY_CHILD_SOURCE (object));
+
+	source = RB_LIBRARY_CHILD_SOURCE (object);
+
+	g_return_if_fail (source->priv != NULL);
+
+	g_free (source->priv->path);
+	g_free (source->priv->browser_key);
+	g_free (source->priv->paned_key);
+
+	G_OBJECT_CLASS (rb_library_child_source_parent_class)->finalize (object);
+}
+
+static void
+rb_library_child_source_set_property (GObject *object,
+					guint prop_id,
+					const GValue *value,
+					GParamSpec *pspec)
+{
+	RBLibraryChildSource *source = RB_LIBRARY_CHILD_SOURCE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		source->priv->path = g_strdup (g_value_get_string (value));
+		char *state_dir;
+		state_dir = get_state_dir (source->priv->path);
+		source->priv->browser_key = g_strconcat (state_dir,
+				CONF_STATE_LIBRARY_CHILD_SHOW_BROWSER, NULL);
+		source->priv->paned_key = g_strconcat (state_dir,
+				CONF_STATE_LIBRARY_CHILD_PANED_POSITION, NULL);
+		g_free (state_dir);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+rb_library_child_source_get_property (GObject *object,
+					guint prop_id,
+					GValue *value,
+					GParamSpec *pspec)
+{
+	RBLibraryChildSource *source = RB_LIBRARY_CHILD_SOURCE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, source->priv->path);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static char *
+get_display_name_for_uri (const char *uri)
+{
+	GFile *file;
+	GFileInfo *file_info;
+	GError *err = NULL;
+	char *name = NULL;
+
+	file = g_file_new_for_uri (uri);
+	file_info = g_file_query_info (file,
+			G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+			G_FILE_QUERY_INFO_NONE,
+			NULL,
+			&err);
+
+	if (file != NULL) {
+		g_object_unref (file);
+	}
+
+	if (file_info == NULL) {
+		if (err != NULL) {
+			rb_debug ("g_file_query_info failed for '%s': %s",
+					uri, err->message);
+			g_error_free (err);
+		} else {
+			rb_debug ("g_file_query_info failed for '%s'", uri);
+		}
+		return NULL;
+	}
+
+	name = g_file_info_get_attribute_as_string (file_info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
+	g_object_unref (file_info);
+	if (name == NULL) {
+		rb_debug ("g_file_info_get_attribute_string failed!");
+	}
+
+	return name;
+}
+
+RBSource *
+rb_library_child_source_new (RBSource *parent_source, const char *path)
+{
+	RBSource *source;
+	RBShell *shell;
+	RhythmDB *db;
+	RhythmDBEntryType *entry_type;
+	GPtrArray *query;
+	GdkPixbuf *icon;
+	char *name;
+	char *state_dir;
+	char *sorting_key;
+
+	name = get_display_name_for_uri (path);
+
+	g_object_get (parent_source,
+			"shell", &shell,
+			"entry-type", &entry_type,
+			"icon", &icon,
+			NULL);
+
+	g_object_get (shell, "db", &db, NULL);
+	query = rhythmdb_query_parse (db,
+			RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, entry_type,
+			RHYTHMDB_QUERY_PROP_PREFIX, RHYTHMDB_PROP_LOCATION, path,
+			RHYTHMDB_QUERY_END);
+	g_object_unref (db);
+
+	state_dir = get_state_dir (path);
+	sorting_key = g_strconcat (state_dir, CONF_STATE_LIBRARY_CHILD_SORTING, NULL);
+	g_free (state_dir);
+
+	source = RB_SOURCE (g_object_new (RB_TYPE_LIBRARY_CHILD_SOURCE,
+					"name", name,
+					"shell", shell,
+					"path", path,
+					"entry-type", entry_type,
+					"sorting-key", sorting_key,
+					"source-group", RB_SOURCE_GROUP_LIBRARY,
+					"icon", icon,
+					"query", query,
+					NULL));
+
+	if (name != NULL) {
+		g_free (name);
+	}
+	g_object_unref (shell);
+	rhythmdb_query_free (query);
+	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+	g_free (sorting_key);
+	if (icon != NULL) {
+		g_object_unref (icon);
+	}
+
+	return source;
+}
+
+static char *
+get_state_dir (const char *path)
+{
+	char *state_dir;
+	char *path_escaped;
+
+	path_escaped = gconf_escape_key (path, strlen (path));
+	state_dir = g_strconcat (CONF_STATE_LIBRARY_DIR, "/", path_escaped, NULL);
+	g_free (path_escaped);
+
+	return state_dir;
+}
+
+static char *
+impl_get_browser_key (RBSource *source)
+{
+	RBLibraryChildSourcePrivate *priv;
+	priv = RB_LIBRARY_CHILD_SOURCE_GET_PRIVATE (RB_LIBRARY_CHILD_SOURCE (source));
+
+	return g_strdup (priv->browser_key);
+}
+
+static char *
+impl_get_paned_key (RBBrowserSource *source)
+{
+	RBLibraryChildSourcePrivate *priv;
+	priv = RB_LIBRARY_CHILD_SOURCE_GET_PRIVATE (RB_LIBRARY_CHILD_SOURCE (source));
+
+	return g_strdup (priv->paned_key);
+}
+
+static gboolean
+delete_entry (GtkTreeModel *model,
+		GtkTreePath *path,
+		GtkTreeIter *iter,
+		RhythmDB *db)
+{
+	RhythmDBEntry *entry;
+	int position;
+
+	gtk_tree_model_get (model, iter, 0, &entry, 1, &position, -1);
+
+	rb_debug ("deleting: %2d - '%s' - '%s' - '%s'",
+			position,
+			rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
+			rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM),
+			rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+
+	rhythmdb_entry_delete (db, entry);
+
+	rhythmdb_entry_unref (entry);
+
+	return FALSE;
+}
+
+void
+rb_library_child_source_set_keep_songs_and_state (RBLibraryChildSource *source,
+							gboolean keep)
+{
+	RBLibraryChildSourcePrivate *priv;
+	priv = RB_LIBRARY_CHILD_SOURCE_GET_PRIVATE (source);
+
+	priv->keep_songs_and_state = keep;
+}
+
+static void
+impl_delete_thyself (RBSource *source)
+{
+	RBLibraryChildSourcePrivate *priv;
+
+	priv = RB_LIBRARY_CHILD_SOURCE_GET_PRIVATE (RB_LIBRARY_CHILD_SOURCE (source));
+
+	if (priv->keep_songs_and_state == TRUE) {
+		rb_debug ("keeping songs and state.");
+		RB_SOURCE_CLASS (rb_library_child_source_parent_class)->impl_delete_thyself (source);
+		return;
+	}
+
+	rb_debug ("deleting songs and state.");
+
+	RBShell *shell;
+	RhythmDBQueryModel *model;
+	RhythmDB *db;
+	char *path;
+	char *state_dir;
+
+	g_object_get (source,
+			"shell", &shell,
+			"base-query-model", &model,
+			"path", &path,
+			NULL);
+	g_object_get (shell, "db", &db, NULL);
+	g_object_unref (shell);
+
+	/* remove songs from db */
+	gtk_tree_model_foreach (GTK_TREE_MODEL (model),
+			(GtkTreeModelForeachFunc)delete_entry,
+			db);
+	rhythmdb_commit (db);
+	g_object_unref (db);
+
+	state_dir = get_state_dir (path);
+	g_free (path);
+
+	/* remove state gconf keys */
+	eel_gconf_recursive_unset (state_dir);
+	eel_gconf_suggest_sync ();
+	g_free (state_dir);
+
+	RB_SOURCE_CLASS (rb_library_child_source_parent_class)->impl_delete_thyself (source);
+}
diff --git a/sources/rb-library-child-source.h b/sources/rb-library-child-source.h
new file mode 100644
index 0000000..7362f19
--- /dev/null
+++ b/sources/rb-library-child-source.h
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (C) 2008 Christian Becke <christianbecke@gmail.com>
+ *
+ *  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.
+ *
+ *  The Rhythmbox authors hereby grants permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#ifndef __RB_LIBRARY_CHILD_SOURCE_H
+#define __RB_LIBRARY_CHILD_SOURCE_H
+
+#include "rb-browser-source.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_LIBRARY_CHILD_SOURCE         (rb_library_child_source_get_type ())
+#define RB_LIBRARY_CHILD_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_LIBRARY_CHILD_SOURCE, RBLibraryChildSource))
+#define RB_LIBRARY_CHILD_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_LIBRARY_CHILD_SOURCE, RBLibraryChildSourceClass))
+#define RB_IS_LIBRARY_CHILD_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_LIBRARY_CHILD_SOURCE))
+#define RB_IS_LIBRARY_CHILD_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_LIBRARY_CHILD_SOURCE))
+#define RB_LIBRARY_CHILD_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_LIBRARY_CHILD_SOURCE, RBLibraryChildSourceClass))
+
+typedef struct _RBLibraryChildSourcePrivate RBLibraryChildSourcePrivate;
+
+typedef struct
+{
+	RBBrowserSource parent;
+
+	RBLibraryChildSourcePrivate *priv;
+} RBLibraryChildSource;
+
+typedef struct
+{
+	RBBrowserSourceClass parent;
+} RBLibraryChildSourceClass;
+
+GType		rb_library_child_source_get_type 	(void);
+RBSource *	rb_library_child_source_new		(RBSource *parent_source, const char *path);
+void		rb_library_child_source_set_keep_songs_and_state (RBLibraryChildSource *source, gboolean keep);
+G_END_DECLS
+
+#endif /* __RB_LIBRARY_CHILD_SOURCE_H */
diff --git a/sources/rb-library-file-helpers.c b/sources/rb-library-file-helpers.c
new file mode 100644
index 0000000..277322a
--- /dev/null
+++ b/sources/rb-library-file-helpers.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
+ *  Copyright (C) 2003,2004 Colin Walters <walters@verbum.org>
+ *
+ *  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.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include "config.h"
+
+#include <profiles/gnome-media-profiles.h>
+
+#include "rb-debug.h"
+#include "eel-gconf-extensions.h"
+#include "rb-library-file-helpers.h"
+
+/**
+ * Perform magic on a path to make it safe.
+ *
+ * This will always replace '/' with '-', and optionally make the file name
+ * shell-friendly. This involves removing replacing shell metacharacters and all
+ * whitespace with '_'. Also any leading periods are removed so that the files
+ * don't end up being hidden.
+ */
+static char *
+sanitize_path (const char *str)
+{
+	gchar *s;
+
+	/* Skip leading periods, otherwise files disappear... */
+	while (*str == '.')
+		str++;
+
+	s = g_strdup(str);
+	/* Replace path seperators with a hyphen */
+	g_strdelimit (s, "/", '-');
+	if (eel_gconf_get_boolean (CONF_LIBRARY_STRIP_CHARS)) {
+		/* Replace separators with a hyphen */
+		g_strdelimit (s, "\\:|", '-');
+		/* Replace all other weird characters to whitespace */
+		g_strdelimit (s, "*?&!\'\"$()`>{}", ' ');
+		/* Replace all whitespace with underscores */
+		/* TODO: I'd like this to compress whitespace aswell */
+		g_strdelimit (s, "\t ", '_');
+	}
+
+	return s;
+}
+
+char *
+rb_library_sanitize_pattern (const char *pat)
+{
+	if (eel_gconf_get_boolean (CONF_LIBRARY_STRIP_CHARS)) {
+		gchar *s;
+
+		s = g_strdup (pat);
+		g_strdelimit (s, "\t ", '_');
+		return s;
+	} else {
+		return g_strdup (pat);
+	}
+}
+
+/*
+ * Parse a filename pattern and replace markers with values from a RhythmDBEntry
+ *
+ * Valid markers so far are:
+ * %at -- album title
+ * %aa -- album artist
+ * %aA -- album artist (lowercase)
+ * %as -- album artist sortname
+ * %aS -- album artist sortname (lowercase)
+ * %tn -- track number (i.e 8)
+ * %tN -- track number, zero padded (i.e 08)
+ * %tt -- track title
+ * %ta -- track artist
+ * %tA -- track artist (lowercase)
+ * %ts -- track artist sortname
+ * %tS -- track artist sortname (lowercase)
+ */
+char *
+rb_library_filepath_parse_pattern (const char *pattern,
+			RhythmDBEntry *entry)
+{
+	/* p is the pattern iterator, i is a general purpose iterator */
+	const char *p;
+	char *temp;
+	GString *s;
+
+	if (pattern == NULL || pattern[0] == 0)
+		return g_strdup (" ");
+
+	s = g_string_new (NULL);
+
+	p = pattern;
+	while (*p) {
+		char *string = NULL;
+		char *t;
+
+		/* If not a % marker, copy and continue */
+		if (*p != '%') {
+			g_string_append_c (s, *p++);
+			/* Explicit increment as we continue past the increment */
+			continue;
+		}
+
+		/* Is a % marker, go to next and see what to do */
+		switch (*++p) {
+		case '%':
+			/*
+			 * Literal %
+			 */
+			g_string_append_c (s, '%');
+			break;
+		case 'a':
+			/*
+			 * Album tag
+			 */
+			switch (*++p) {
+			case 't':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+				break;
+			case 'T':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_FOLDED));
+				break;
+			case 'a':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+				break;
+			case 'A':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_FOLDED));
+				break;
+			case 's':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME));
+				break;
+			case 'S':
+				t = g_utf8_strdown (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME), -1);
+				string = sanitize_path (t);
+				g_free (t);
+				break;
+			default:
+				string = g_strdup_printf ("%%a%c", *p);
+			}
+
+			break;
+
+		case 't':
+			/*
+			 * Track tag
+			 */
+			switch (*++p) {
+			case 't':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+				break;
+			case 'T':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE_FOLDED));
+				break;
+			case 'a':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+				break;
+			case 'A':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_FOLDED));
+				break;
+			case 's':
+				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME));
+				break;
+			case 'S':
+				t = g_utf8_strdown (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME), -1);
+				string = sanitize_path (t);
+				g_free (t);
+				break;
+			case 'n':
+				/* Track number */
+				string = g_strdup_printf ("%u", (guint)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
+				break;
+			case 'N':
+				/* Track number, zero-padded */
+				string = g_strdup_printf ("%02u", (guint)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
+				break;
+			default:
+				string = g_strdup_printf ("%%t%c", *p);
+			}
+
+			break;
+
+		default:
+			string = g_strdup_printf ("%%%c", *p);
+		}
+
+		if (string)
+			g_string_append (s, string);
+		g_free (string);
+
+		++p;
+	}
+
+	temp = s->str;
+	g_string_free (s, FALSE);
+	return temp;
+}
+
+/**
+ * Build the absolute filename for the specified track.
+ *
+ * The base path is the extern variable 'base_path', the format to use
+ * is the extern variable 'file_pattern'. Free the result when you
+ * have finished with it.
+ *
+ * Stolen from Sound-Juicer
+ */
+char*
+rb_library_build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
+{
+	GFile *library_location;
+	GFile *dir;
+	GFile *dest;
+	char *realfile;
+	char *realpath;
+	char *filename;
+	char *string = NULL;
+	char *extension = NULL;
+	char *tmp;
+	char *default_library_location;
+	char *layout_path;
+	char *layout_filename;
+	char *preferred_format;
+
+	default_library_location = eel_gconf_get_string (CONF_DEFAULT_LIBRARY_LOCATION);
+	layout_path = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH);
+	layout_filename = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME);
+	preferred_format = eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT);
+
+	if (default_library_location == NULL || layout_path == NULL || layout_filename == NULL || preferred_format == NULL) {
+		/* emit warning */
+		rb_debug ("Could not retrieve settings from GConf");
+		goto out;
+	}
+
+	tmp = rb_library_sanitize_pattern (layout_filename);
+	g_free (layout_filename);
+	layout_filename = tmp;
+
+	realpath = rb_library_filepath_parse_pattern (layout_path, entry);
+
+	library_location = g_file_new_for_uri (default_library_location);
+	dir = g_file_resolve_relative_path (library_location, realpath);
+	g_object_unref (library_location);
+	g_free (realpath);
+
+	if (g_str_has_prefix (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE), "audio/x-raw")) {
+		GMAudioProfile *profile;
+		profile = gm_audio_profile_lookup (preferred_format);
+		if (profile)
+			extension = g_strdup (gm_audio_profile_get_extension (profile));
+	}
+
+	if (extension == NULL) {
+		const char *uri;
+		const char *loc;
+		char *tmp;
+
+		/* use the old extension. strip anything after a '?' for http/daap/etc */
+		uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+		loc = g_utf8_strrchr (uri, -1, '.');
+		if (loc == NULL)
+			loc = g_utf8_strrchr (uri, -1, '/');
+		if (loc == NULL)
+			loc = uri;
+
+		extension = g_strdup (loc + 1);
+
+		tmp = g_utf8_strchr (extension, -1, '?');
+		if (tmp)
+			*tmp = '\0';
+	}
+
+	realfile = rb_library_filepath_parse_pattern (layout_filename, entry);
+	if (extension) {
+		filename = g_strdup_printf ("%s.%s", realfile, extension);
+		g_free (realfile);
+	} else {
+		filename = realfile;
+	}
+
+	dest = g_file_resolve_relative_path (dir, filename);
+	g_object_unref (dir);
+	g_free (extension);
+	g_free (filename);
+
+	string = g_file_get_uri (dest);
+	g_object_unref (dest);
+ out:
+	g_free (default_library_location);
+	g_free (layout_path);
+	g_free (layout_filename);
+	g_free (preferred_format);
+
+	return string;
+}
diff --git a/sources/rb-library-file-helpers.h b/sources/rb-library-file-helpers.h
new file mode 100644
index 0000000..a6873d0
--- /dev/null
+++ b/sources/rb-library-file-helpers.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2009 Christian Becke <christianbecke@gmail.com>
+ *
+ *  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.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#ifndef __RB_LIBRARY_FILE_HELPERS_H
+#define __RB_LIBRARY_FILE_HELPERS_H
+
+#include <glib.h>
+
+#include "rhythmdb.h"
+#include "rb-library-source.h"
+
+G_BEGIN_DECLS
+
+char * rb_library_sanitize_pattern (const char *pattern);
+
+char * rb_library_filepath_parse_pattern (const char *pattern, RhythmDBEntry *entry);
+
+char * rb_library_build_filename (RBLibrarySource *source, RhythmDBEntry *entry);
+
+G_END_DECLS
+
+#endif /* __RB_LIBRARY_FILE_HELPERS_H */
diff --git a/sources/rb-library-preferences.c b/sources/rb-library-preferences.c
new file mode 100644
index 0000000..a98bda8
--- /dev/null
+++ b/sources/rb-library-preferences.c
@@ -0,0 +1,992 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
+ *  Copyright (C) 2003,2004 Colin Walters <walters@verbum.org>
+ *  Copyright (C) 2009 Christian Becke <christianbecke@gmail.com>
+ *
+ *  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.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include <profiles/gnome-media-profiles.h>
+
+#include "rhythmdb.h"
+#include "rb-debug.h"
+#include "rb-dialog.h"
+#include "rb-glade-helpers.h"
+#include "rb-util.h"
+#include "rb-library-file-helpers.h"
+#include "rb-library-preferences.h"
+#include "rb-library-child-source.h"
+
+typedef struct {
+	char *uri;
+	char *reason;
+} LocationError;
+
+/* widget callbacks */
+static void rb_library_prefs_edit_profile_clicked_cb (GtkButton *button,
+						       RBLibrarySource *source);
+static void rb_library_prefs_path_changed_cb (GtkComboBox *box,
+						RBLibrarySource *source);
+static void rb_library_prefs_filename_changed_cb (GtkComboBox *box,
+						   RBLibrarySource *source);
+static void rb_library_prefs_format_changed_cb (GtkWidget *widget,
+						 RBLibrarySource *source);
+static void rb_library_prefs_add_location_button_clicked_cb (GtkButton *button,
+		RBLibrarySource *source);
+static void rb_library_prefs_remove_location_button_clicked_cb (GtkButton *button,
+		RBLibrarySource *source);
+static void rb_library_prefs_default_location_toggled_cb (GtkCellRendererToggle *cell,
+		const char *path,
+		RBLibrarySource *source);
+static void rb_library_prefs_monitor_library_locations_toggled_cb (GtkCellRendererToggle *cell,
+		const char *path,
+		RBLibrarySource *source);
+
+/* dialogs */
+static GSList *run_location_file_chooser (RBLibrarySource *source);
+static void show_invalid_locations_error_dialog (RBLibrarySource *source,
+		GSList *invalid_locations);
+static gint show_remove_library_location_message_dialog (RBLibrarySource *source,
+		GtkWidget *keep_metadata_button);
+
+static gboolean location_is_valid (GSList *library_locations,
+		const char *uri,
+		char **reason);
+static void layout_example_label_update (RBLibrarySource *source);
+static void rb_library_prefs_sync (RBLibrarySource *source);
+static void location_error_free (LocationError *error, gpointer userdata);
+
+typedef struct {
+	char *title;
+	char *path;
+} LibraryPathElement;
+
+const LibraryPathElement library_layout_paths[] = {
+	{N_("Artist/Artist - Album"), "%aa/%aa - %at"},
+	{N_("Artist/Album"), "%aa/%at"},
+	{N_("Artist - Album"), "%aa - %at"},
+	{N_("Album"), "%at"},
+	{N_("Artist"), "%aa"},
+};
+const int num_library_layout_paths = G_N_ELEMENTS (library_layout_paths);
+
+const LibraryPathElement library_layout_filenames[] = {
+	{N_("Number - Title"), "%tN - %tt"},
+	{N_("Artist - Title"), "%ta - %tt"},
+	{N_("Artist - Number - Title"), "%ta - %tN - %tt"},
+	{N_("Artist (Album) - Number - Title"), "%ta (%at) - %tN - %tt"},
+	{N_("Title"), "%tt"},
+	{N_("Number. Artist - Title"), "%tN. %ta - %tt"},
+};
+const int num_library_layout_filenames = G_N_ELEMENTS (library_layout_filenames);
+
+enum {
+	LOCATION_COLUMN_PATH,
+	LOCATION_COLUMN_DEFAULT_LIBRARY_LOCATION,
+	LOCATION_COLUMN_MONITOR_LIBRARY_LOCATIONS,
+	LOCATION_N_COLUMNS
+};
+
+GtkWidget *
+rb_library_prefs_get_config_widget (RBSource *asource, RBShellPreferences *prefs)
+{
+	RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
+	GtkWidget *tmp;
+	GladeXML *xml;
+	GtkWidget *label;
+	int i;
+	GtkListStore *liststore;
+	GtkCellRenderer *cell;
+	GtkTreeViewColumn *column;
+	GtkTreeSelection *selection;
+
+	rb_debug ("impl_get_config_widget");
+
+	if (source->priv->config_widget)
+		return source->priv->config_widget;
+
+
+	g_object_ref (G_OBJECT (prefs));
+	source->priv->shell_prefs = prefs;
+
+	xml = rb_glade_xml_new ("library-prefs.glade", "library_vbox", source);
+	source->priv->config_widget =
+		glade_xml_get_widget (xml, "library_vbox");
+
+	rb_glade_boldify_label (xml, "library_location_label");
+
+	source->priv->locations_tree = glade_xml_get_widget (xml, "library_locations_treeview");
+	liststore = gtk_list_store_new (LOCATION_N_COLUMNS,
+			G_TYPE_STRING,
+			G_TYPE_BOOLEAN,
+			G_TYPE_BOOLEAN);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (source->priv->locations_tree), GTK_TREE_MODEL (liststore));
+	g_object_unref (G_OBJECT (liststore));
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source->priv->locations_tree));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+	cell = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes (_("Locations"),
+			cell,
+			"text", LOCATION_COLUMN_PATH,
+			NULL);
+	gtk_tree_view_column_set_expand (column, TRUE);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (source->priv->locations_tree), column);
+
+	cell = gtk_cell_renderer_toggle_new ();
+	gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (cell), TRUE);
+	g_signal_connect (G_OBJECT (cell),
+		"toggled",
+		G_CALLBACK (rb_library_prefs_default_location_toggled_cb),
+		asource);
+	column = gtk_tree_view_column_new_with_attributes (_("Default Location"),
+		cell,
+		"active", LOCATION_COLUMN_DEFAULT_LIBRARY_LOCATION,
+		NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (source->priv->locations_tree), column);
+
+	cell = gtk_cell_renderer_toggle_new ();
+	g_signal_connect (G_OBJECT (cell),
+			"toggled",
+			G_CALLBACK (rb_library_prefs_monitor_library_locations_toggled_cb),
+			asource);
+	column = gtk_tree_view_column_new_with_attributes (_("Watch for new files"),
+			cell,
+			"active", LOCATION_COLUMN_MONITOR_LIBRARY_LOCATIONS,
+			NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (source->priv->locations_tree), column);
+
+	tmp = glade_xml_get_widget (xml, "add_library_location_button");
+	g_signal_connect (G_OBJECT (tmp),
+			"clicked",
+			G_CALLBACK (rb_library_prefs_add_location_button_clicked_cb),
+			asource);
+
+	tmp = glade_xml_get_widget (xml, "remove_library_location_button");
+	g_signal_connect (G_OBJECT (tmp),
+			"clicked",
+			G_CALLBACK (rb_library_prefs_remove_location_button_clicked_cb),
+			asource);
+
+	rb_glade_boldify_label (xml, "library_structure_label");
+
+	tmp = glade_xml_get_widget (xml, "layout_path_menu_box");
+	label = glade_xml_get_widget (xml, "layout_path_menu_label");
+	source->priv->layout_path_menu = gtk_combo_box_new_text ();
+	gtk_box_pack_start (GTK_BOX (tmp), source->priv->layout_path_menu, TRUE, TRUE, 0);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->layout_path_menu);
+	g_signal_connect (G_OBJECT (source->priv->layout_path_menu),
+			  "changed",
+			  G_CALLBACK (rb_library_prefs_path_changed_cb),
+			  asource);
+	for (i = 0; i < num_library_layout_paths; i++) {
+		gtk_combo_box_append_text (GTK_COMBO_BOX (source->priv->layout_path_menu),
+					   _(library_layout_paths[i].title));
+	}
+
+	tmp = glade_xml_get_widget (xml, "layout_filename_menu_box");
+	label = glade_xml_get_widget (xml, "layout_filename_menu_label");
+	source->priv->layout_filename_menu = gtk_combo_box_new_text ();
+	gtk_box_pack_start (GTK_BOX (tmp), source->priv->layout_filename_menu, TRUE, TRUE, 0);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->layout_filename_menu);
+	g_signal_connect (G_OBJECT (source->priv->layout_filename_menu),
+			  "changed",
+			  G_CALLBACK (rb_library_prefs_filename_changed_cb),
+			  asource);
+	for (i = 0; i < num_library_layout_filenames; i++) {
+		gtk_combo_box_append_text (GTK_COMBO_BOX (source->priv->layout_filename_menu),
+					   _(library_layout_filenames[i].title));
+	}
+
+	tmp = glade_xml_get_widget (xml, "edit_profile_button");
+	g_signal_connect (G_OBJECT (tmp),
+			  "clicked",
+			  G_CALLBACK (rb_library_prefs_edit_profile_clicked_cb),
+			  asource);
+
+	tmp = glade_xml_get_widget (xml, "preferred_format_menu_box");
+	label = glade_xml_get_widget (xml, "preferred_format_menu_label");
+	source->priv->preferred_format_menu = gm_audio_profile_choose_new ();
+	gtk_box_pack_start (GTK_BOX (tmp), source->priv->preferred_format_menu, TRUE, TRUE, 0);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->preferred_format_menu);
+	g_signal_connect (G_OBJECT (source->priv->preferred_format_menu),
+			  "changed",
+			  G_CALLBACK (rb_library_prefs_format_changed_cb),
+			  asource);
+
+	source->priv->layout_example_label = glade_xml_get_widget (xml, "layout_example_label");
+
+	g_object_unref (G_OBJECT (xml));
+
+	rb_library_prefs_sync (source);
+
+	return source->priv->config_widget;
+}
+
+void
+rb_library_prefs_library_location_changed (GConfClient *client,
+					    guint cnxn_id,
+					    GConfEntry *entry,
+					    RBLibrarySource *source)
+{
+	rb_debug ("library locations changed");
+
+	rb_library_prefs_sync_default_library_location (source, FALSE);
+	rb_library_prefs_sync_monitored_locations (source, FALSE);
+
+	if (source->priv->config_widget)
+		rb_library_prefs_sync (source);
+
+	rb_library_source_sync_child_sources (source);
+}
+
+void
+rb_library_prefs_default_library_location_changed (GConfClient *client,
+					    guint cnxn_id,
+					    GConfEntry *entry,
+					    RBLibrarySource *source)
+{
+	rb_debug ("default library location changed");
+
+	rb_library_prefs_sync_default_library_location (source, TRUE);
+
+	if (source->priv->config_widget)
+		rb_library_prefs_sync (source);
+}
+
+void
+rb_library_prefs_monitor_library_locations_changed (GConfClient *client,
+					    guint cnxn_id,
+					    GConfEntry *entry,
+					    RBLibrarySource *source)
+{
+	rb_debug ("monitored library locations changed");
+
+	rb_library_prefs_sync_monitored_locations (source, TRUE);
+
+	if (source->priv->config_widget)
+		rb_library_prefs_sync (source);
+}
+
+void
+rb_library_prefs_ui_pref_changed (GConfClient *client,
+				   guint cnxn_id,
+				   GConfEntry *entry,
+				   RBLibrarySource *source)
+{
+	rb_debug ("ui pref changed");
+	rb_library_prefs_ui_prefs_sync (source);
+}
+
+void
+rb_library_prefs_layout_path_changed (GConfClient *client,
+				       guint cnxn_id,
+				       GConfEntry *entry,
+				       RBLibrarySource *source)
+{
+	char *value;
+	int i = 0;
+
+	g_return_if_fail (strcmp (entry->key, CONF_LIBRARY_LAYOUT_PATH) == 0);
+
+	rb_debug ("layout path changed");
+
+	if (entry->value == NULL) {
+		value = g_strdup (library_layout_paths[0].path);
+	} else if (entry->value->type == GCONF_VALUE_STRING) {
+		value = g_strdup (gconf_value_get_string (entry->value));
+	} else {
+		return;
+	}
+
+	while (library_layout_paths[i].path && strcmp (library_layout_paths[i].path, value) != 0) {
+		i++;
+	}
+
+	g_free (value);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (source->priv->layout_path_menu), i);
+
+	layout_example_label_update (source);
+}
+
+void
+rb_library_prefs_layout_filename_changed (GConfClient *client,
+					   guint cnxn_id,
+					   GConfEntry *entry,
+					   RBLibrarySource *source)
+{
+	char *value;
+	int i = 0;
+
+	g_return_if_fail (strcmp (entry->key, CONF_LIBRARY_LAYOUT_FILENAME) == 0);
+
+	rb_debug ("layout filename changed");
+
+	if (entry->value == NULL) {
+		value = g_strdup (library_layout_filenames[0].path);
+	} else if (entry->value->type == GCONF_VALUE_STRING) {
+		value = g_strdup (gconf_value_get_string (entry->value));
+	} else {
+		return;
+	}
+
+	while (library_layout_filenames[i].path && strcmp (library_layout_filenames[i].path, value) != 0) {
+		i++;
+	}
+
+	g_free (value);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (source->priv->layout_filename_menu), i);
+
+	layout_example_label_update (source);
+}
+
+static GSList *
+run_location_file_chooser (RBLibrarySource *source)
+{
+	GtkWidget *dialog;
+	GSList *uris = NULL;
+
+	rb_debug ("running location file chooser dialog");
+
+	dialog = rb_file_chooser_new (_("Choose Library Location"), GTK_WINDOW (source->priv->shell_prefs),
+			GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, FALSE);
+	gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+		uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
+	}
+	gtk_widget_destroy (dialog);
+	return uris;
+}
+
+static void
+show_invalid_locations_error_dialog (RBLibrarySource *source, GSList *invalid_locations)
+{
+	GtkListStore *model;
+	GSList *i;
+	LocationError *err;
+	GtkTreeIter it;
+	char *tmp;
+	GtkWidget *dialog, *content_area, *sw, *tree;
+	GtkTreeSelection *sel;
+	GtkCellRenderer *cell;
+	GtkTreeViewColumn *col;
+
+	model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+	for (i = invalid_locations; i != NULL; i = g_slist_next (i)) {
+		err = (LocationError *)i->data;
+
+		g_assert (err != NULL && err->uri != NULL && err->reason != NULL);
+
+		gtk_list_store_append (model, &it);
+
+		tmp = g_uri_unescape_string (err->uri, NULL);
+		gtk_list_store_set (model, &it,
+				0, tmp,
+				1, err->reason,
+				-1);
+		g_free (tmp);
+	}
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (source->priv->shell_prefs),
+			GTK_DIALOG_DESTROY_WITH_PARENT,
+			GTK_MESSAGE_ERROR,
+			GTK_BUTTONS_CLOSE,
+			"%s",
+			_("Some locations could not be added"));
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+			"%s",
+			_("The following locations could not be added to the list of library locations:"));
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+	g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+
+	content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+			GTK_POLICY_NEVER,
+			GTK_POLICY_ALWAYS);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
+	gtk_box_pack_end (GTK_BOX (content_area), sw, TRUE, TRUE, 6);
+
+	tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+	g_object_unref (G_OBJECT (model));
+	gtk_widget_set_size_request (tree, -1, 150);
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
+	gtk_container_add (GTK_CONTAINER (sw), tree);
+
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
+	gtk_tree_selection_set_mode (sel, GTK_SELECTION_NONE);
+
+	cell = gtk_cell_renderer_text_new ();
+	col = gtk_tree_view_column_new_with_attributes (_("Location"),
+			cell,
+			"text", 0,
+			NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), col);
+
+	cell = gtk_cell_renderer_text_new ();
+	g_object_set (G_OBJECT (cell),
+			"wrap-width", 500,
+			"wrap-mode", PANGO_WRAP_WORD,
+			NULL);
+	col = gtk_tree_view_column_new_with_attributes (_("Reason"),
+			cell,
+			"text", 1,
+			NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), col);
+
+	gtk_widget_show_all (dialog);
+}
+
+static gint
+show_remove_library_location_message_dialog (RBLibrarySource *source, GtkWidget *keep_metadata_button)
+{
+	GtkWidget *dialog;
+	GtkWidget *content_area;
+	gint ret;
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (source->priv->shell_prefs),
+			GTK_DIALOG_DESTROY_WITH_PARENT,
+			GTK_MESSAGE_QUESTION,
+			GTK_BUTTONS_OK_CANCEL,
+			"%s", _("Remove Library Location"));
+
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+			"%s", _("Rhythmbox will no longer track changes for the removed library location.\n"
+				"If you want to remove all tracks stored in this location from your library, "
+				"and remove all meta data (e.g. ratings, playcounts) for these tracks as well, "
+				"uncheck the check button below.\n"
+				"No files will actually be deleted."));
+
+	content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+	g_object_ref (G_OBJECT (keep_metadata_button));
+	gtk_box_pack_end (GTK_BOX (content_area), keep_metadata_button, FALSE, FALSE, 6);
+	gtk_widget_show (keep_metadata_button);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+
+	ret = gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+	return ret;
+}
+
+static void
+rb_library_prefs_edit_profile_clicked_cb (GtkButton *button, RBLibrarySource *source)
+{
+	GtkWidget *dialog;
+
+	dialog = gm_audio_profiles_edit_new (eel_gconf_client_get_global (),
+					     GTK_WINDOW (source->priv->shell_prefs));
+	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+	gtk_widget_show_all (dialog);
+	gtk_dialog_run (GTK_DIALOG (dialog));
+}
+
+static void
+rb_library_prefs_add_location_button_clicked_cb (GtkButton *button, RBLibrarySource *source)
+{
+	GSList *uris, *i;
+	GSList *invalid_locations = NULL;
+	GSList *library_locations;
+	char *uri;
+	char *reason;
+	LocationError *err;
+	gboolean location_added = FALSE;
+
+	rb_debug ("add_location button clicked");
+
+	library_locations = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+
+	uris = run_location_file_chooser (source);
+	for (i = uris; i != NULL; i = g_slist_next (i)) {
+		uri = (char *)i->data;
+
+		g_assert (uri != NULL);
+
+		if (location_is_valid (library_locations, uri, &reason)) {
+			library_locations = g_slist_prepend (library_locations, g_strdup (uri));
+			location_added = TRUE;
+			rb_debug ("location '%s' added", uri);
+		} else {
+			rb_debug ("invalid location '%s': %s", uri, reason);
+			err = g_new (LocationError, 1);
+			err->uri = g_strdup (uri);
+			err->reason = g_strdup (reason);
+			invalid_locations = g_slist_prepend (invalid_locations, err);
+			g_free (reason);
+		}
+	}
+	rb_slist_deep_free (uris);
+
+	if (location_added) {
+		eel_gconf_set_string_list (CONF_LIBRARY_LOCATION, library_locations);
+	}
+	rb_slist_deep_free (library_locations);
+
+	if (g_slist_length (invalid_locations) > 0) {
+		show_invalid_locations_error_dialog (source, invalid_locations);
+	}
+	g_slist_foreach (invalid_locations, (GFunc)location_error_free, NULL);
+	g_slist_free (invalid_locations);
+}
+
+static void
+rb_library_prefs_monitor_library_locations_toggled_cb (GtkCellRendererToggle *cell,
+		const char *path,
+		RBLibrarySource *source)
+{
+	GtkTreeModel *model;
+	GtkTreeIter it;
+	char *uri;
+	gboolean do_monitor;
+	GSList *monitor_locations, *found;
+	gboolean update = FALSE;
+
+	rb_debug ("monitor library location toggled");
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (source->priv->locations_tree));
+
+	g_assert (gtk_tree_model_get_iter_from_string (model, &it, path));
+
+	gtk_tree_model_get (model, &it,
+			LOCATION_COLUMN_PATH, &uri,
+			LOCATION_COLUMN_MONITOR_LIBRARY_LOCATIONS, &do_monitor,
+			-1);
+
+	g_assert (uri != NULL);
+
+	do_monitor = (!do_monitor); /* toggle! */
+
+	monitor_locations = eel_gconf_get_string_list (CONF_MONITOR_LIBRARY_LOCATIONS);
+	found = g_slist_find_custom (monitor_locations, uri, (GCompareFunc)strcmp);
+	if (do_monitor) {
+		if (found == NULL) {
+			monitor_locations = g_slist_prepend (monitor_locations, g_strdup (uri));
+			update = TRUE;
+		}
+	} else {
+		if (found != NULL) {
+			monitor_locations = g_slist_delete_link (monitor_locations, found);
+			update = TRUE;
+		}
+	}
+	g_free (uri);
+
+	if (update) {
+		eel_gconf_set_string_list (CONF_MONITOR_LIBRARY_LOCATIONS, monitor_locations);
+	}
+	rb_slist_deep_free (monitor_locations);
+}
+
+static void
+rb_library_prefs_default_location_toggled_cb (GtkCellRendererToggle *cell,
+		const char *path,
+		RBLibrarySource *source)
+{
+	GtkTreeModel *model;
+	GtkTreeIter it;
+	char *uri;
+
+	rb_debug ("default dest toggled");
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (source->priv->locations_tree));
+
+	g_assert (gtk_tree_model_get_iter_from_string (model, &it, path));
+
+	gtk_tree_model_get (model, &it, LOCATION_COLUMN_PATH, &uri, -1);
+
+	g_assert (uri != NULL);
+
+	eel_gconf_set_string (CONF_DEFAULT_LIBRARY_LOCATION, uri);
+	g_free (uri);
+}
+
+static void
+rb_library_prefs_remove_location_button_clicked_cb (GtkButton *button, RBLibrarySource *source)
+{
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GList *selected_rows, *i;
+	GtkWidget *keep_metadata_button;
+	gboolean location_removed = FALSE;
+
+	rb_debug ("removing location");
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source->priv->locations_tree));
+	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+	keep_metadata_button = gtk_check_button_new_with_mnemonic (_("_Keep tracks and meta data"));
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (keep_metadata_button), TRUE);
+	if (GTK_RESPONSE_OK == show_remove_library_location_message_dialog (source, keep_metadata_button)) {
+		GSList *library_locations, *found;
+		gboolean keep_metadata;
+		GtkTreePath *path;
+		GtkTreeIter it;
+		char *uri;
+		RBLibraryChildSource *child_source;
+
+		library_locations = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+
+		keep_metadata = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (keep_metadata_button));
+
+		for (i = selected_rows; i != NULL; i = g_list_next (i)) {
+			path = (GtkTreePath *)i->data;
+
+			g_assert (gtk_tree_model_get_iter (model, &it, path));
+
+			gtk_tree_model_get (model, &it, LOCATION_COLUMN_PATH, &uri, -1);
+
+			g_assert (uri != NULL);
+
+			found = g_slist_find_custom (library_locations, uri, (GCompareFunc)strcmp);
+			if (found != NULL) {
+				/* remove location */
+				child_source = rb_library_source_get_child_source_for_uri (source, uri);
+				if (child_source != NULL) {
+					rb_debug ("setting keep_metadata for child_source [%p] to %s",
+							child_source, (keep_metadata)?"true":"false");
+					rb_library_child_source_set_keep_songs_and_state (child_source, keep_metadata);
+				}
+
+				library_locations = g_slist_delete_link (library_locations, found);
+				location_removed = TRUE;
+				rb_debug ("location '%s' removed", uri);
+			}
+
+			g_free (uri);
+		}
+
+		if (location_removed) {
+			eel_gconf_set_string_list (CONF_LIBRARY_LOCATION, library_locations);
+		}
+		rb_slist_deep_free (library_locations);
+	}
+	gtk_widget_destroy (keep_metadata_button);
+	rb_list_destroy_free (selected_rows, (GDestroyNotify)gtk_tree_path_free);
+}
+
+static void
+rb_library_prefs_path_changed_cb (GtkComboBox *box, RBLibrarySource *source)
+{
+	const char *path;
+	gint index;
+
+	index = gtk_combo_box_get_active (box);
+	path = (index >= 0) ? library_layout_paths[index].path : "";
+	eel_gconf_set_string (CONF_LIBRARY_LAYOUT_PATH, path);
+}
+
+static void
+rb_library_prefs_filename_changed_cb (GtkComboBox *box, RBLibrarySource *source)
+{
+	const char *filename;
+	gint index;
+
+	index = gtk_combo_box_get_active (box);
+	filename = (index >= 0) ? library_layout_filenames[index].path : "";
+	eel_gconf_set_string (CONF_LIBRARY_LAYOUT_FILENAME, filename);
+}
+
+static void
+rb_library_prefs_format_changed_cb (GtkWidget *widget, RBLibrarySource *source)
+{
+	GMAudioProfile *profile;
+
+	profile = gm_audio_profile_choose_get_active (widget);
+	eel_gconf_set_string (CONF_LIBRARY_PREFERRED_FORMAT, gm_audio_profile_get_id (profile));
+
+	layout_example_label_update (source);
+}
+
+static gboolean
+location_is_valid (GSList *library_locations, const char *uri, char **reason)
+{
+	GSList *i;
+	gboolean is_valid = TRUE;
+	GFile *new, *tmp;
+	char *tmp_uri, *tmp_uri_unescaped;
+
+	g_assert (reason != NULL);
+
+	*reason = NULL;
+
+	new = g_file_new_for_uri (uri);
+	for (i = library_locations; i != NULL; i = g_slist_next (i)) {
+		tmp = g_file_new_for_uri ((char *)i->data);
+		if (g_file_equal (tmp, new)) {
+			*reason = g_strdup (_("The location is already in your library."));
+			is_valid = FALSE;
+		} else if (g_file_has_prefix (tmp, new)) {
+			tmp_uri = g_file_get_uri (tmp);
+			tmp_uri_unescaped = g_uri_unescape_string (tmp_uri, NULL);
+			g_free (tmp_uri);
+			*reason = g_strdup_printf (_("Subdirectory '%s' of this location is already in your library. "
+						"Nested library locations are not allowed."),
+					tmp_uri_unescaped);
+			g_free (tmp_uri_unescaped);
+			is_valid = FALSE;
+		} else if (g_file_has_prefix (new, tmp)) {
+			tmp_uri = g_file_get_uri (tmp);
+			tmp_uri_unescaped = g_uri_unescape_string (tmp_uri, NULL);
+			g_free (tmp_uri);
+			*reason = g_strdup_printf (_("This is a subdirectory of '%s', which is already in your library. "
+						"Nested library locations are not allowed."),
+					tmp_uri_unescaped);
+			g_free (tmp_uri_unescaped);
+			is_valid = FALSE;
+		}
+		g_object_unref (G_OBJECT (tmp));
+		if (!is_valid) {
+			break;
+		}
+	}
+	g_object_unref (G_OBJECT (new));
+
+	return is_valid;
+}
+
+void
+rb_library_prefs_sync_default_library_location (RBLibrarySource *source, gboolean disable_notification)
+{
+	gboolean removed = FALSE;
+	GSList *library_locations;
+	char *default_library_location;
+
+	if (disable_notification && source->priv->default_library_location_notify_id != 0) {
+		eel_gconf_notification_remove (source->priv->default_library_location_notify_id);
+		removed = TRUE;
+	}
+
+	library_locations = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+	default_library_location = eel_gconf_get_string (CONF_DEFAULT_LIBRARY_LOCATION);
+	if (default_library_location == NULL || !rb_string_slist_contains (library_locations, default_library_location)) {
+		if (library_locations != NULL && library_locations->data != NULL) {
+			eel_gconf_set_string (CONF_DEFAULT_LIBRARY_LOCATION, (char *)library_locations->data);
+		} else {
+			eel_gconf_set_string (CONF_DEFAULT_LIBRARY_LOCATION, "");
+		}
+	}
+	g_free (default_library_location);
+	rb_slist_deep_free (library_locations);
+
+	if (removed) {
+		source->priv->default_library_location_notify_id =
+			eel_gconf_notification_add (CONF_DEFAULT_LIBRARY_LOCATION,
+					(GConfClientNotifyFunc) rb_library_prefs_default_library_location_changed,
+					source);
+	}
+}
+
+void
+rb_library_prefs_sync_monitored_locations (RBLibrarySource *source, gboolean disable_notification)
+{
+	gboolean removed = FALSE;
+	GSList *monitored_locations;
+
+	if (disable_notification && source->priv->monitor_library_locations_notify_id != 0) {
+		eel_gconf_notification_remove (source->priv->monitor_library_locations_notify_id);
+		removed = TRUE;
+	}
+
+	monitored_locations = eel_gconf_get_string_list (CONF_MONITOR_LIBRARY_LOCATIONS);
+	if (monitored_locations != NULL) {
+		GSList *library_locations, *i, *remove = NULL;
+		gboolean location_removed = FALSE;
+
+		library_locations = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+
+		for (i = monitored_locations; i != NULL; i = g_slist_next (i)) {
+			if (!rb_string_slist_contains (library_locations, (char *)i->data)) {
+				remove = g_slist_prepend (remove, i);
+				location_removed = TRUE;
+			}
+		}
+		rb_slist_deep_free (library_locations);
+
+		for (i = remove; i != NULL; i = g_slist_next (i)) {
+			monitored_locations = g_slist_remove_link (monitored_locations, (GSList *)i->data);
+		}
+
+		if (location_removed) {
+			eel_gconf_set_string_list (CONF_MONITOR_LIBRARY_LOCATIONS, monitored_locations);
+		}
+	}
+	rb_slist_deep_free (monitored_locations);
+
+	if (removed) {
+		source->priv->monitor_library_locations_notify_id =
+			eel_gconf_notification_add (CONF_MONITOR_LIBRARY_LOCATIONS,
+					(GConfClientNotifyFunc) rb_library_prefs_monitor_library_locations_changed,
+					source);
+	}
+}
+
+void
+rb_library_prefs_ui_prefs_sync (RBLibrarySource *source)
+{
+	if (source->priv->config_widget)
+		rb_library_prefs_sync (source);
+}
+
+static void
+rb_library_prefs_sync (RBLibrarySource *source)
+{
+	GSList *list, *i;
+	char *str;
+	GConfClient *gconf_client;
+	GtkTreeModel *model;
+	GtkTreeIter it;
+	char *uri;
+	char *default_dest_uri;
+	gboolean is_default_dest;
+	GSList *monitor_locations;
+	gboolean is_monitored;
+
+	rb_debug ("syncing pref dialog state");
+
+	/* library location */
+	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+	list = g_slist_reverse (list);
+	default_dest_uri = eel_gconf_get_string (CONF_DEFAULT_LIBRARY_LOCATION);
+	monitor_locations = eel_gconf_get_string_list (CONF_MONITOR_LIBRARY_LOCATIONS);
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (source->priv->locations_tree));
+	gtk_list_store_clear (GTK_LIST_STORE (model));
+	for (i = list; i != NULL; i = g_slist_next (i)) {
+		uri = (char *)i->data;
+		is_default_dest = (uri != NULL && default_dest_uri != NULL && strcmp (uri, default_dest_uri) == 0)? TRUE : FALSE;
+		is_monitored = rb_string_slist_contains (monitor_locations, uri);
+		gtk_list_store_append (GTK_LIST_STORE (model), &it);
+		gtk_list_store_set (GTK_LIST_STORE (model), &it,
+				LOCATION_COLUMN_PATH, uri,
+				LOCATION_COLUMN_DEFAULT_LIBRARY_LOCATION, is_default_dest,
+				LOCATION_COLUMN_MONITOR_LIBRARY_LOCATIONS, is_monitored,
+				-1);
+	}
+	g_free (default_dest_uri);
+	rb_slist_deep_free (list);
+	rb_slist_deep_free (monitor_locations);
+
+	/* preferred format */
+	str = eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT);
+	if (str) {
+		gm_audio_profile_choose_set_active (source->priv->preferred_format_menu, str);
+		g_free (str);
+	}
+
+	source->priv->layout_path_notify_id =
+		eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_PATH,
+				    (GConfClientNotifyFunc) rb_library_prefs_layout_path_changed, source);
+	source->priv->layout_filename_notify_id =
+		eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_FILENAME,
+				    (GConfClientNotifyFunc) rb_library_prefs_layout_filename_changed, source);
+
+	gconf_client = eel_gconf_client_get_global ();
+	/* layout path */
+	rb_library_prefs_layout_path_changed (gconf_client, -1,
+					       gconf_client_get_entry (gconf_client, CONF_LIBRARY_LAYOUT_PATH, NULL, TRUE, NULL),
+					       source);
+	/* layout filename */
+	rb_library_prefs_layout_filename_changed (gconf_client, -1,
+						   gconf_client_get_entry (gconf_client, CONF_LIBRARY_LAYOUT_FILENAME, NULL, TRUE, NULL),
+						   source);
+}
+
+static void
+layout_example_label_update (RBLibrarySource *source)
+{
+	char *file_pattern;
+	char *path_pattern;
+	char *file_value;
+	char *path_value;
+	char *example;
+	char *format;
+	char *tmp;
+	GMAudioProfile *profile;
+	RhythmDBEntryType entry_type;
+	RhythmDBEntry *sample_entry;
+
+	profile = gm_audio_profile_choose_get_active (source->priv->preferred_format_menu);
+
+	/* TODO: sucky. Replace with get-gconf-key-with-default mojo */
+	file_pattern = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME);
+	if (file_pattern == NULL) {
+		file_pattern = g_strdup (library_layout_filenames[0].path);
+	}
+	tmp = rb_library_sanitize_pattern (file_pattern);
+	g_free (file_pattern);
+	file_pattern = tmp;
+
+	path_pattern = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH);
+	if (path_pattern == NULL) {
+		path_pattern = g_strdup (library_layout_paths[0].path);
+	}
+
+	g_object_get (source, "entry-type", &entry_type, NULL);
+	sample_entry = rhythmdb_entry_example_new (source->priv->db, entry_type, NULL);
+	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+
+	file_value = rb_library_filepath_parse_pattern (file_pattern, sample_entry);
+	path_value = rb_library_filepath_parse_pattern (path_pattern, sample_entry);
+	rhythmdb_entry_unref (sample_entry);
+
+	example = g_build_filename (G_DIR_SEPARATOR_S, path_value, file_value, NULL);
+	g_free (file_value);
+	g_free (file_pattern);
+	g_free (path_value);
+	g_free (path_pattern);
+
+	format = g_strconcat ("<small><i><b>",
+			      _("Example Path:"),
+			      "</b> ",
+			      example,
+			      ".",
+			      profile ? gm_audio_profile_get_extension (profile) : "ogg",
+			      "</i></small>", NULL);
+	g_free (example);
+
+	gtk_label_set_markup (GTK_LABEL (source->priv->layout_example_label), format);
+	g_free (format);
+}
+
+static void
+location_error_free (LocationError *error, gpointer userdata)
+{
+	g_return_if_fail (error != NULL);
+
+	g_free (error->uri);
+	g_free (error->reason);
+}
diff --git a/sources/rb-library-preferences.h b/sources/rb-library-preferences.h
new file mode 100644
index 0000000..c0c347a
--- /dev/null
+++ b/sources/rb-library-preferences.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2009 Christian Becke <christianbecke@gmail.com>
+ *
+ *  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.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#ifndef __RB_LIBRARY_PREFERENCES_H
+#define __RB_LIBRARY_PREFERENCES_H
+
+#include <gtk/gtk.h>
+
+#include "eel-gconf-extensions.h"
+#include "rb-shell.h"
+#include "rb-library-source.h"
+
+G_BEGIN_DECLS
+
+#define CONF_UI_LIBRARY_DIR CONF_PREFIX "/ui/library"
+#define CONF_STATE_LIBRARY_DIR CONF_PREFIX "/state/library"
+#define CONF_STATE_LIBRARY_SORTING CONF_PREFIX "/state/library/sorting"
+#define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/library/paned_position"
+#define CONF_STATE_SHOW_BROWSER   CONF_PREFIX "/state/library/show_browser"
+
+GtkWidget *rb_library_prefs_get_config_widget (RBSource *source, RBShellPreferences *prefs);
+
+/* gconf notification functions */
+void rb_library_prefs_library_location_changed (GConfClient *client,
+						    guint cnxn_id,
+						    GConfEntry *entry,
+						    RBLibrarySource *source);
+void rb_library_prefs_layout_path_changed (GConfClient *client,
+						   guint cnxn_id,
+						   GConfEntry *entry,
+						   RBLibrarySource *source);
+void rb_library_prefs_layout_filename_changed (GConfClient *client,
+						       guint cnxn_id,
+						       GConfEntry *entry,
+						       RBLibrarySource *source);
+void rb_library_prefs_ui_pref_changed (GConfClient *client,
+					       guint cnxn_id,
+					       GConfEntry *entry,
+					       RBLibrarySource *source);
+void rb_library_prefs_monitor_library_locations_changed (GConfClient *client,
+					    guint cnxn_id,
+					    GConfEntry *entry,
+					    RBLibrarySource *source);
+void rb_library_prefs_default_library_location_changed (GConfClient *client,
+					    guint cnxn_id,
+					    GConfEntry *entry,
+					    RBLibrarySource *source);
+
+/* functions for syncing ui and gconf state */
+void rb_library_prefs_ui_prefs_sync (RBLibrarySource *source);
+void rb_library_prefs_sync_default_library_location (RBLibrarySource *source, gboolean disable_notification);
+void rb_library_prefs_sync_monitored_locations (RBLibrarySource *source, gboolean disable_notification);
+
+G_END_DECLS
+
+#endif /* __RB_LIBRARY_PREFERENCES_H */
diff --git a/sources/rb-library-source.c b/sources/rb-library-source.c
index 75b697b..2f14a1a 100644
--- a/sources/rb-library-source.c
+++ b/sources/rb-library-source.c
@@ -55,18 +55,17 @@
 #include <glib-object.h>
 
 #include <profiles/gnome-media-profiles.h>
-#include <profiles/audio-profile-choose.h>
 
 #include "rhythmdb.h"
 #include "rb-debug.h"
-#include "rb-dialog.h"
-#include "rb-glade-helpers.h"
 #include "rb-file-helpers.h"
 #include "rb-util.h"
 #include "eel-gconf-extensions.h"
 #include "rb-library-source.h"
+#include "rb-library-preferences.h"
+#include "rb-library-file-helpers.h"
 #include "rb-removable-media-manager.h"
-#include "rb-auto-playlist-source.h"
+#include "rb-library-child-source.h"
 
 static void rb_library_source_class_init (RBLibrarySourceClass *klass);
 static void rb_library_source_init (RBLibrarySource *source);
@@ -78,7 +77,6 @@ static void rb_library_source_finalize (GObject *object);
 
 /* RBSource implementations */
 static gboolean impl_show_popup (RBSource *source);
-static GtkWidget *impl_get_config_widget (RBSource *source, RBShellPreferences *prefs);
 static char *impl_get_browser_key (RBSource *source);
 static char *impl_get_paned_key (RBBrowserSource *source);
 static gboolean impl_receive_drag (RBSource *source, GtkSelectionData *data);
@@ -87,94 +85,11 @@ static void impl_paste (RBSource *source, GList *entries);
 static guint impl_want_uri (RBSource *source, const char *uri);
 static gboolean impl_add_uri (RBSource *source, const char *uri, const char *title, const char *genre);
 
-static void rb_library_source_ui_prefs_sync (RBLibrarySource *source);
-static void rb_library_source_preferences_sync (RBLibrarySource *source);
-
-static void rb_library_source_library_location_changed (GConfClient *client,
-						    guint cnxn_id,
-						    GConfEntry *entry,
-						    RBLibrarySource *source);
-static void rb_library_source_layout_path_changed (GConfClient *client,
-						   guint cnxn_id,
-						   GConfEntry *entry,
-						   RBLibrarySource *source);
-static void rb_library_source_layout_filename_changed (GConfClient *client,
-						       guint cnxn_id,
-						       GConfEntry *entry,
-						       RBLibrarySource *source);
-static void rb_library_source_edit_profile_clicked_cb (GtkButton *button,
-						       RBLibrarySource *source);
-static void rb_library_source_ui_pref_changed (GConfClient *client,
-					       guint cnxn_id,
-					       GConfEntry *entry,
-					       RBLibrarySource *source);
-static gboolean rb_library_source_library_location_cb (GtkEntry *entry,
-						       GdkEventFocus *event,
-						       RBLibrarySource *source);
-static void rb_library_source_watch_toggled_cb (GtkToggleButton *button,
-						RBLibrarySource *source);
-static void rb_library_source_sync_child_sources (RBLibrarySource *source);
-static void rb_library_source_path_changed_cb (GtkComboBox *box,
-						RBLibrarySource *source);
-static void rb_library_source_filename_changed_cb (GtkComboBox *box,
-						   RBLibrarySource *source);
-static void rb_library_source_format_changed_cb (GtkWidget *widget,
-						 RBLibrarySource *source);
-static void layout_example_label_update (RBLibrarySource *source);
-
-#define CONF_UI_LIBRARY_DIR CONF_PREFIX "/ui/library"
-#define CONF_STATE_LIBRARY_DIR CONF_PREFIX "/state/library"
-#define CONF_STATE_LIBRARY_SORTING CONF_PREFIX "/state/library/sorting"
-#define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/library/paned_position"
-#define CONF_STATE_SHOW_BROWSER   CONF_PREFIX "/state/library/show_browser"
-
-typedef struct {
-	char *title;
-	char *path;
-} LibraryPathElement;
-
-const LibraryPathElement library_layout_paths[] = {
-	{N_("Artist/Artist - Album"), "%aa/%aa - %at"},
-	{N_("Artist/Album"), "%aa/%at"},
-	{N_("Artist - Album"), "%aa - %at"},
-	{N_("Album"), "%at"},
-	{N_("Artist"), "%aa"},
-};
-const int num_library_layout_paths = G_N_ELEMENTS (library_layout_paths);
-
-const LibraryPathElement library_layout_filenames[] = {
-	{N_("Number - Title"), "%tN - %tt"},
-	{N_("Artist - Title"), "%ta - %tt"},
-	{N_("Artist - Number - Title"), "%ta - %tN - %tt"},
-	{N_("Artist (Album) - Number - Title"), "%ta (%at) - %tN - %tt"},
-	{N_("Title"), "%tt"},
-	{N_("Number. Artist - Title"), "%tN. %ta - %tt"},
-};
-const int num_library_layout_filenames = G_N_ELEMENTS (library_layout_filenames);
-
-struct RBLibrarySourcePrivate
-{
-	RhythmDB *db;
-
-	gboolean loading_prefs;
-	RBShellPreferences *shell_prefs;
-
-	GtkWidget *config_widget;
-
-	GList *child_sources;
-
-	GtkWidget *library_location_entry;
-	GtkWidget *watch_library_check;
-	GtkWidget *layout_path_menu;
-	GtkWidget *layout_filename_menu;
-	GtkWidget *preferred_format_menu;
-	GtkWidget *layout_example_label;
-
-	guint library_location_notify_id;
-	guint ui_dir_notify_id;
-	guint layout_path_notify_id;
-	guint layout_filename_notify_id;
-};
+static void rb_library_source_add_child_source (RBLibrarySource *library_source,
+		const char *path);
+static void rb_library_source_remove_child_source (RBLibrarySource *library_source,
+		RBLibraryChildSource *child_source);
+void rb_library_source_sync_child_sources (RBLibrarySource *source);
 
 #define RB_LIBRARY_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_LIBRARY_SOURCE, RBLibrarySourcePrivate))
 G_DEFINE_TYPE (RBLibrarySource, rb_library_source, RB_TYPE_BROWSER_SOURCE)
@@ -191,7 +106,7 @@ rb_library_source_class_init (RBLibrarySourceClass *klass)
 	object_class->constructor = rb_library_source_constructor;
 
 	source_class->impl_show_popup = impl_show_popup;
-	source_class->impl_get_config_widget = impl_get_config_widget;
+	source_class->impl_get_config_widget = rb_library_prefs_get_config_widget;
 	source_class->impl_get_browser_key = impl_get_browser_key;
 	source_class->impl_receive_drag = impl_receive_drag;
 	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
@@ -240,6 +155,11 @@ rb_library_source_dispose (GObject *object)
 		source->priv->library_location_notify_id = 0;
 	}
 
+	if (source->priv->default_library_location_notify_id != 0) {
+		eel_gconf_notification_remove (source->priv->default_library_location_notify_id);
+		source->priv->default_library_location_notify_id = 0;
+	}
+
 	if (source->priv->layout_path_notify_id != 0) {
 		eel_gconf_notification_remove (source->priv->layout_path_notify_id);
 		source->priv->layout_path_notify_id = 0;
@@ -250,6 +170,11 @@ rb_library_source_dispose (GObject *object)
 		source->priv->layout_filename_notify_id = 0;
 	}
 
+	if (source->priv->monitor_library_locations_notify_id != 0) {
+		eel_gconf_notification_remove (source->priv->monitor_library_locations_notify_id);
+		source->priv->monitor_library_locations_notify_id = 0;
+	}
+
 	G_OBJECT_CLASS (rb_library_source_parent_class)->dispose (object);
 }
 
@@ -295,7 +220,7 @@ rb_library_source_constructor (GType type,
 	RBLibrarySource *source;
 	RBShell *shell;
 	RBEntryView *songs;
-	GSList *list;
+	GSList *list, *monitor_locations;
 
 	source = RB_LIBRARY_SOURCE (G_OBJECT_CLASS (rb_library_source_parent_class)
 			->constructor (type, n_construct_properties, construct_properties));
@@ -305,9 +230,9 @@ rb_library_source_constructor (GType type,
 
 	g_signal_connect_object (source->priv->db, "load-complete", G_CALLBACK (db_load_complete_cb), source, 0);
 
-	rb_library_source_ui_prefs_sync (source);
+	rb_library_prefs_ui_prefs_sync (source);
 
-	/* Set up the default library location if there's no library location set */
+	/* Set up a library location if there's no library location set */
 	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
 	if (g_slist_length (list) == 0) {
 		char *music_dir_uri;
@@ -340,15 +265,40 @@ rb_library_source_constructor (GType type,
 			eel_gconf_set_string_list (CONF_LIBRARY_LOCATION, list);
 		}
 	}
+
+	/* migrate CONF_MONITOR_LIBRARY setting */
+	monitor_locations = eel_gconf_get_string_list (CONF_MONITOR_LIBRARY_LOCATIONS);
+	if (monitor_locations == NULL && eel_gconf_get_boolean (CONF_MONITOR_LIBRARY)) {
+		rb_debug ("CONF_MONITOR_LIBRARY was set, migrating all library locations to CONF_MONITOR_LIBRARY_LOCATIONS");
+		eel_gconf_set_string_list (CONF_MONITOR_LIBRARY_LOCATIONS, list);
+	}
+	rb_slist_deep_free (monitor_locations);
 	rb_slist_deep_free (list);
 
+	/* make sure migration is only done once */
+	eel_gconf_recursive_unset (CONF_MONITOR_LIBRARY);
+
+	/* make sure default library location is sane */
+	rb_library_prefs_sync_default_library_location (source, TRUE);
+
+	/* make sure monitored library locations are sane */
+	rb_library_prefs_sync_monitored_locations (source, TRUE);
+
 	source->priv->library_location_notify_id =
 		eel_gconf_notification_add (CONF_LIBRARY_LOCATION,
-				    (GConfClientNotifyFunc) rb_library_source_library_location_changed, source);
+				    (GConfClientNotifyFunc) rb_library_prefs_library_location_changed, source);
+
+	source->priv->default_library_location_notify_id =
+		eel_gconf_notification_add (CONF_DEFAULT_LIBRARY_LOCATION,
+				(GConfClientNotifyFunc) rb_library_prefs_default_library_location_changed, source);
 
 	source->priv->ui_dir_notify_id =
 		eel_gconf_notification_add (CONF_UI_LIBRARY_DIR,
-				    (GConfClientNotifyFunc) rb_library_source_ui_pref_changed, source);
+				    (GConfClientNotifyFunc) rb_library_prefs_ui_pref_changed, source);
+
+	source->priv->monitor_library_locations_notify_id =
+		eel_gconf_notification_add (CONF_MONITOR_LIBRARY_LOCATIONS,
+				(GConfClientNotifyFunc) rb_library_prefs_monitor_library_locations_changed, source);
 
 	songs = rb_source_get_entry_view (RB_SOURCE (source));
 
@@ -404,270 +354,37 @@ rb_library_source_new (RBShell *shell)
 	return source;
 }
 
-static void
-rb_library_source_edit_profile_clicked_cb (GtkButton *button, RBLibrarySource *source)
-{
-	GtkWidget *dialog;
-
-	dialog = gm_audio_profiles_edit_new (eel_gconf_client_get_global (),
-					     GTK_WINDOW (source->priv->shell_prefs));
-	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
-	gtk_widget_show_all (dialog);
-	gtk_dialog_run (GTK_DIALOG (dialog));
-}
-
-static void
-rb_library_source_location_button_clicked_cb (GtkButton *button, RBLibrarySource *source)
-{
-	GtkWidget *dialog;
-
-	dialog = rb_file_chooser_new (_("Choose Library Location"), GTK_WINDOW (source->priv->shell_prefs),
-				      GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, FALSE);
-	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
-		char *uri;
-		char *path;
-
-		uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
-		if (uri == NULL) {
-			uri = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
-		}
-
-		path = g_uri_unescape_string (uri, NULL);
-
-		gtk_entry_set_text (GTK_ENTRY (source->priv->library_location_entry), path);
-		rb_library_source_library_location_cb (GTK_ENTRY (source->priv->library_location_entry),
-						       NULL, source);
-		g_free (uri);
-		g_free (path);
-	}
-
-	gtk_widget_destroy (GTK_WIDGET (dialog));
-}
-
-static GtkWidget *
-impl_get_config_widget (RBSource *asource, RBShellPreferences *prefs)
-{
-	RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
-	GtkWidget *tmp;
-	GladeXML *xml;
-	GtkWidget *label;
-	int i;
-
-	if (source->priv->config_widget)
-		return source->priv->config_widget;
-
-	g_object_ref (G_OBJECT (prefs));
-	source->priv->shell_prefs = prefs;
-
-	xml = rb_glade_xml_new ("library-prefs.glade", "library_vbox", source);
-	source->priv->config_widget =
-		glade_xml_get_widget (xml, "library_vbox");
-
-	rb_glade_boldify_label (xml, "library_location_label");
-
-	source->priv->library_location_entry = glade_xml_get_widget (xml, "library_location_entry");
-	tmp = glade_xml_get_widget (xml, "library_location_button");
-	g_signal_connect (G_OBJECT (tmp),
-			  "clicked",
-			  G_CALLBACK (rb_library_source_location_button_clicked_cb),
-			  asource);
-	g_signal_connect (G_OBJECT (source->priv->library_location_entry),
-			  "focus-out-event",
-			  G_CALLBACK (rb_library_source_library_location_cb),
-			  asource);
-
-	source->priv->watch_library_check = glade_xml_get_widget (xml, "watch_library_check");
-	g_signal_connect (G_OBJECT (source->priv->watch_library_check),
-			  "toggled",
-			  G_CALLBACK (rb_library_source_watch_toggled_cb),
-			  asource);
-
-	rb_glade_boldify_label (xml, "library_structure_label");
-
-	tmp = glade_xml_get_widget (xml, "layout_path_menu_box");
-	label = glade_xml_get_widget (xml, "layout_path_menu_label");
-	source->priv->layout_path_menu = gtk_combo_box_new_text ();
-	gtk_box_pack_start (GTK_BOX (tmp), source->priv->layout_path_menu, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->layout_path_menu);
-	g_signal_connect (G_OBJECT (source->priv->layout_path_menu),
-			  "changed",
-			  G_CALLBACK (rb_library_source_path_changed_cb),
-			  asource);
-	for (i = 0; i < num_library_layout_paths; i++) {
-		gtk_combo_box_append_text (GTK_COMBO_BOX (source->priv->layout_path_menu),
-					   _(library_layout_paths[i].title));
-	}
-
-	tmp = glade_xml_get_widget (xml, "layout_filename_menu_box");
-	label = glade_xml_get_widget (xml, "layout_filename_menu_label");
-	source->priv->layout_filename_menu = gtk_combo_box_new_text ();
-	gtk_box_pack_start (GTK_BOX (tmp), source->priv->layout_filename_menu, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->layout_filename_menu);
-	g_signal_connect (G_OBJECT (source->priv->layout_filename_menu),
-			  "changed",
-			  G_CALLBACK (rb_library_source_filename_changed_cb),
-			  asource);
-	for (i = 0; i < num_library_layout_filenames; i++) {
-		gtk_combo_box_append_text (GTK_COMBO_BOX (source->priv->layout_filename_menu),
-					   _(library_layout_filenames[i].title));
-	}
-
-	tmp = glade_xml_get_widget (xml, "edit_profile_button");
-	g_signal_connect (G_OBJECT (tmp),
-			  "clicked",
-			  G_CALLBACK (rb_library_source_edit_profile_clicked_cb),
-			  asource);
-
-	tmp = glade_xml_get_widget (xml, "preferred_format_menu_box");
-	label = glade_xml_get_widget (xml, "preferred_format_menu_label");
-	source->priv->preferred_format_menu = gm_audio_profile_choose_new ();
-	gtk_box_pack_start (GTK_BOX (tmp), source->priv->preferred_format_menu, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->preferred_format_menu);
-	g_signal_connect (G_OBJECT (source->priv->preferred_format_menu),
-			  "changed",
-			  G_CALLBACK (rb_library_source_format_changed_cb),
-			  asource);
-
-	source->priv->layout_example_label = glade_xml_get_widget (xml, "layout_example_label");
-
-	g_object_unref (G_OBJECT (xml));
-
-	rb_library_source_preferences_sync (source);
-
-	return source->priv->config_widget;
-}
-
-static void
-rb_library_source_library_location_changed (GConfClient *client,
-					    guint cnxn_id,
-					    GConfEntry *entry,
-					    RBLibrarySource *source)
-{
-	if (source->priv->config_widget)
-		rb_library_source_preferences_sync (source);
-
-	rb_library_source_sync_child_sources (source);
-}
-
-static void
-rb_library_source_ui_prefs_sync (RBLibrarySource *source)
-{
-	if (source->priv->config_widget)
-		rb_library_source_preferences_sync (source);
-}
-
-static void
-rb_library_source_ui_pref_changed (GConfClient *client,
-				   guint cnxn_id,
-				   GConfEntry *entry,
-				   RBLibrarySource *source)
-{
-	rb_debug ("ui pref changed");
-	rb_library_source_ui_prefs_sync (source);
-}
-
-static void
-rb_library_source_preferences_sync (RBLibrarySource *source)
+RBLibraryChildSource *
+rb_library_source_get_child_source_for_uri (RBLibrarySource *source, const char *uri)
 {
-	GSList *list;
-	char *str;
-	GConfClient *gconf_client;
-
-	rb_debug ("syncing pref dialog state");
+	GList *i;
+	RBLibraryChildSource *child_source, *found = NULL;
+	char *path;
 
-	/* library location */
-	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+	rb_debug ("searching child_source for uri '%s'", uri);
 
-	/* don't trigger the change notification */
-	g_signal_handlers_block_by_func (G_OBJECT (source->priv->library_location_entry),
-					 G_CALLBACK (rb_library_source_library_location_cb),
-					 source);
+	g_return_val_if_fail (uri != NULL, NULL);
 
-	if (g_slist_length (list) == 1) {
-		char *path;
+	for (i = source->priv->child_sources; i != NULL; i = g_list_next (i)) {
+		child_source = (RBLibraryChildSource *)i->data;
+		g_object_get (G_OBJECT (child_source),
+				"path", &path,
+				NULL);
 
-		gtk_widget_set_sensitive (source->priv->library_location_entry, TRUE);
+		g_assert (path != NULL);
 
-		path = g_uri_unescape_string (list->data, NULL);
-		gtk_entry_set_text (GTK_ENTRY (source->priv->library_location_entry), path);
+		if (strcmp (uri, path) == 0) {
+			rb_debug ("found child_source [%p]", child_source);
+			found = child_source;
+		}
 		g_free (path);
-	} else if (g_slist_length (list) == 0) {
-		/* no library directories */
-		gtk_widget_set_sensitive (source->priv->library_location_entry, TRUE);
-		gtk_entry_set_text (GTK_ENTRY (source->priv->library_location_entry), "");
-	} else {
-		/* multiple library directories */
-		gtk_widget_set_sensitive (source->priv->library_location_entry, FALSE);
-		gtk_entry_set_text (GTK_ENTRY (source->priv->library_location_entry), _("Multiple locations set"));
-	}
-
-	g_signal_handlers_unblock_by_func (G_OBJECT (source->priv->library_location_entry),
-					   G_CALLBACK (rb_library_source_library_location_cb),
-					   source);
-
-	rb_slist_deep_free (list);
 
-	/* watch checkbox */
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (source->priv->watch_library_check),
-				      eel_gconf_get_boolean (CONF_MONITOR_LIBRARY));
-
-	/* preferred format */
-	str = eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT);
-	if (str) {
-		gm_audio_profile_choose_set_active (source->priv->preferred_format_menu, str);
-		g_free (str);
+		if (found != NULL) {
+			break;
+		}
 	}
 
-	source->priv->layout_path_notify_id =
-		eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_PATH,
-				    (GConfClientNotifyFunc) rb_library_source_layout_path_changed, source);
-	source->priv->layout_filename_notify_id =
-		eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_FILENAME,
-				    (GConfClientNotifyFunc) rb_library_source_layout_filename_changed, source);
-
-	gconf_client = eel_gconf_client_get_global ();
-	/* layout path */
-	rb_library_source_layout_path_changed (gconf_client, -1,
-					       gconf_client_get_entry (gconf_client, CONF_LIBRARY_LAYOUT_PATH, NULL, TRUE, NULL),
-					       source);
-	/* layout filename */
-	rb_library_source_layout_filename_changed (gconf_client, -1,
-						   gconf_client_get_entry (gconf_client, CONF_LIBRARY_LAYOUT_FILENAME, NULL, TRUE, NULL),
-						   source);
-}
-
-static gboolean
-rb_library_source_library_location_cb (GtkEntry *entry,
-				       GdkEventFocus *event,
-				       RBLibrarySource *source)
-{
-	GSList *list = NULL;
-	const char *path;
-	GFile *file;
-	char *uri;
-
-	path = gtk_entry_get_text (entry);
-	file = g_file_parse_name (path);
-	uri = g_file_get_uri (file);
-	g_object_unref (file);
-
-	if (uri && uri[0])
-		list = g_slist_prepend (NULL, (gpointer)uri);
-
-	eel_gconf_set_string_list (CONF_LIBRARY_LOCATION, list);
-
-	rb_slist_deep_free (list);
-
-	return FALSE;
-}
-
-static void
-rb_library_source_watch_toggled_cb (GtkToggleButton *button, RBLibrarySource *source)
-{
-	gboolean active;
-
-	active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (source->priv->watch_library_check));
-	eel_gconf_set_boolean (CONF_MONITOR_LIBRARY, active);
+	return found;
 }
 
 static char *
@@ -731,451 +448,16 @@ impl_show_popup (RBSource *source)
 	return TRUE;
 }
 
-static void
-rb_library_source_path_changed_cb (GtkComboBox *box, RBLibrarySource *source)
-{
-	const char *path;
-	gint index;
-
-	index = gtk_combo_box_get_active (box);
-	path = (index >= 0) ? library_layout_paths[index].path : "";
-	eel_gconf_set_string (CONF_LIBRARY_LAYOUT_PATH, path);
-}
-
-static void
-rb_library_source_filename_changed_cb (GtkComboBox *box, RBLibrarySource *source)
-{
-	const char *filename;
-	gint index;
-
-	index = gtk_combo_box_get_active (box);
-	filename = (index >= 0) ? library_layout_filenames[index].path : "";
-	eel_gconf_set_string (CONF_LIBRARY_LAYOUT_FILENAME, filename);
-}
-
-static void
-rb_library_source_format_changed_cb (GtkWidget *widget, RBLibrarySource *source)
-{
-	GMAudioProfile *profile;
-
-	profile = gm_audio_profile_choose_get_active (widget);
-	eel_gconf_set_string (CONF_LIBRARY_PREFERRED_FORMAT, gm_audio_profile_get_id (profile));
-	
-	layout_example_label_update (source);
-}
-
-/**
- * Perform magic on a path to make it safe.
- *
- * This will always replace '/' with '-', and optionally make the file name
- * shell-friendly. This involves removing replacing shell metacharacters and all
- * whitespace with '_'. Also any leading periods are removed so that the files
- * don't end up being hidden.
- */
-static char *
-sanitize_path (const char *str)
-{
-	gchar *s;
-
-	/* Skip leading periods, otherwise files disappear... */
-	while (*str == '.')
-		str++;
-
-	s = g_strdup(str);
-	/* Replace path seperators with a hyphen */
-	g_strdelimit (s, "/", '-');
-	if (eel_gconf_get_boolean (CONF_LIBRARY_STRIP_CHARS)) {
-		/* Replace separators with a hyphen */
-		g_strdelimit (s, "\\:|", '-');
-		/* Replace all other weird characters to whitespace */
-		g_strdelimit (s, "*?&!\'\"$()`>{}", ' ');
-		/* Replace all whitespace with underscores */
-		/* TODO: I'd like this to compress whitespace aswell */
-		g_strdelimit (s, "\t ", '_');
-	}
-
-	return s;
-}
-
-static char *
-sanitize_pattern (const char *pat)
-{
-	if (eel_gconf_get_boolean (CONF_LIBRARY_STRIP_CHARS)) {
-		gchar *s;
-
-		s = g_strdup (pat);
-		g_strdelimit (s, "\t ", '_');
-		return s;
-	} else {
-		return g_strdup (pat);
-	}
-}
-
-/*
- * Parse a filename pattern and replace markers with values from a RhythmDBEntry
- *
- * Valid markers so far are:
- * %at -- album title
- * %aa -- album artist
- * %aA -- album artist (lowercase)
- * %as -- album artist sortname
- * %aS -- album artist sortname (lowercase)
- * %tn -- track number (i.e 8)
- * %tN -- track number, zero padded (i.e 08)
- * %tt -- track title
- * %ta -- track artist
- * %tA -- track artist (lowercase)
- * %ts -- track artist sortname
- * %tS -- track artist sortname (lowercase)
- */
-static char *
-filepath_parse_pattern (const char *pattern,
-			RhythmDBEntry *entry)
-{
-	/* p is the pattern iterator, i is a general purpose iterator */
-	const char *p;
-	char *temp;
-	GString *s;
-
-	if (pattern == NULL || pattern[0] == 0)
-		return g_strdup (" ");
-
-	s = g_string_new (NULL);
-
-	p = pattern;
-	while (*p) {
-		char *string = NULL;
-		char *t;
-
-		/* If not a % marker, copy and continue */
-		if (*p != '%') {
-			g_string_append_c (s, *p++);
-			/* Explicit increment as we continue past the increment */
-			continue;
-		}
-
-		/* Is a % marker, go to next and see what to do */
-		switch (*++p) {
-		case '%':
-			/*
-			 * Literal %
-			 */
-			g_string_append_c (s, '%');
-			break;
-		case 'a':
-			/*
-			 * Album tag
-			 */
-			switch (*++p) {
-			case 't':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
-				break;
-			case 'T':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_FOLDED));
-				break;
-			case 'a':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
-				break;
-			case 'A':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_FOLDED));
-				break;
-			case 's':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME));
-				break;
-			case 'S':
-				t = g_utf8_strdown (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME), -1);
-				string = sanitize_path (t);
-				g_free (t);
-				break;
-			default:
-				string = g_strdup_printf ("%%a%c", *p);
-			}
-
-			break;
-
-		case 't':
-			/*
-			 * Track tag
-			 */
-			switch (*++p) {
-			case 't':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
-				break;
-			case 'T':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE_FOLDED));
-				break;
-			case 'a':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
-				break;
-			case 'A':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_FOLDED));
-				break;
-			case 's':
-				string = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME));
-				break;
-			case 'S':
-				t = g_utf8_strdown (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_SORTNAME), -1);
-				string = sanitize_path (t);
-				g_free (t);
-				break;
-			case 'n':
-				/* Track number */
-				string = g_strdup_printf ("%u", (guint)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
-				break;
-			case 'N':
-				/* Track number, zero-padded */
-				string = g_strdup_printf ("%02u", (guint)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
-				break;
-			default:
-				string = g_strdup_printf ("%%t%c", *p);
- 			}
-
-			break;
-
-		default:
-			string = g_strdup_printf ("%%%c", *p);
-		}
-
-		if (string)
-			g_string_append (s, string);
-		g_free (string);
-
-		++p;
-	}
-
-	temp = s->str;
-	g_string_free (s, FALSE);
-	return temp;
-}
-
-static void
-layout_example_label_update (RBLibrarySource *source)
-{
-	char *file_pattern;
-	char *path_pattern;
-	char *file_value;
-	char *path_value;
-	char *example;
-	char *format;
-	char *tmp;
-	GMAudioProfile *profile;
-	RhythmDBEntryType entry_type;
-	RhythmDBEntry *sample_entry;
-
-  	profile = gm_audio_profile_choose_get_active (source->priv->preferred_format_menu);
-
-	/* TODO: sucky. Replace with get-gconf-key-with-default mojo */
-	file_pattern = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME);
-	if (file_pattern == NULL) {
-		file_pattern = g_strdup (library_layout_filenames[0].path);
-	}
-	tmp = sanitize_pattern (file_pattern);
-	g_free (file_pattern);
-	file_pattern = tmp;
-
-	path_pattern = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH);
-	if (path_pattern == NULL) {
-		path_pattern = g_strdup (library_layout_paths[0].path);
-	}
-
-	g_object_get (source, "entry-type", &entry_type, NULL);
-	sample_entry = rhythmdb_entry_example_new (source->priv->db, entry_type, NULL);
-	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
-
-	file_value = filepath_parse_pattern (file_pattern, sample_entry);
-	path_value = filepath_parse_pattern (path_pattern, sample_entry);
-	rhythmdb_entry_unref (sample_entry);
-
-	example = g_build_filename (G_DIR_SEPARATOR_S, path_value, file_value, NULL);
-	g_free (file_value);
-	g_free (file_pattern);
-	g_free (path_value);
-	g_free (path_pattern);
-
-	format = g_strconcat ("<small><i><b>",
-			      _("Example Path:"),
-			      "</b> ",
-			      example,
-			      ".",
-			      profile ? gm_audio_profile_get_extension (profile) : "ogg",
-			      "</i></small>", NULL);
-	g_free (example);
-
-	gtk_label_set_markup (GTK_LABEL (source->priv->layout_example_label), format);
-	g_free (format);
-}
-
-static void
-rb_library_source_layout_path_changed (GConfClient *client,
-				       guint cnxn_id,
-				       GConfEntry *entry,
-				       RBLibrarySource *source)
-{
-	char *value;
-	int i = 0;
-
-	g_return_if_fail (strcmp (entry->key, CONF_LIBRARY_LAYOUT_PATH) == 0);
-
-	rb_debug ("layout path changed");
-
-	if (entry->value == NULL) {
-		value = g_strdup (library_layout_paths[0].path);
-	} else if (entry->value->type == GCONF_VALUE_STRING) {
-		value = g_strdup (gconf_value_get_string (entry->value));
-	} else {
-		return;
-	}
-
-	while (library_layout_paths[i].path && strcmp (library_layout_paths[i].path, value) != 0) {
-		i++;
-	}
-
-	g_free (value);
-	gtk_combo_box_set_active (GTK_COMBO_BOX (source->priv->layout_path_menu), i);
-
-	layout_example_label_update (source);
-}
-
-static void
-rb_library_source_layout_filename_changed (GConfClient *client,
-					   guint cnxn_id,
-					   GConfEntry *entry,
-					   RBLibrarySource *source)
-{
-	char *value;
-	int i = 0;
-
-	g_return_if_fail (strcmp (entry->key, CONF_LIBRARY_LAYOUT_FILENAME) == 0);
-
-	rb_debug ("layout filename changed");
-
-	if (entry->value == NULL) {
-		value = g_strdup (library_layout_filenames[0].path);
-	} else if (entry->value->type == GCONF_VALUE_STRING) {
-		value = g_strdup (gconf_value_get_string (entry->value));
-	} else {
-		return;
-	}
-
-	while (library_layout_filenames[i].path && strcmp (library_layout_filenames[i].path, value) != 0) {
-		i++;
-	}
-
-	g_free (value);
-	gtk_combo_box_set_active (GTK_COMBO_BOX (source->priv->layout_filename_menu), i);
-
-	layout_example_label_update (source);
-}
-
-/**
- * Build the absolute filename for the specified track.
- *
- * The base path is the extern variable 'base_path', the format to use
- * is the extern variable 'file_pattern'. Free the result when you
- * have finished with it.
- *
- * Stolen from Sound-Juicer
- */
-static char*
-build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
-{
-	GFile *library_location;
-	GFile *dir;
-	GFile *dest;
-	char *realfile;
-	char *realpath;
-	char *filename;
-	char *string = NULL;
-	char *extension = NULL;
-	char *tmp;
-	GSList *list;
-	char *layout_path;
-	char *layout_filename;
-	char *preferred_format;
-
-	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
-	layout_path = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH);
-	layout_filename = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME);
-	preferred_format = eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT);
-
-	if (list == NULL || layout_path == NULL || layout_filename == NULL || preferred_format == NULL) {
-		/* emit warning */
-		rb_debug ("Could not retrieve settings from GConf");
-		goto out;
-	}
-
-	tmp = sanitize_pattern (layout_filename);
-	g_free (layout_filename);
-	layout_filename = tmp;
-
-	realpath = filepath_parse_pattern (layout_path, entry);
-
-	library_location = g_file_new_for_uri ((const char *)list->data);
-	dir = g_file_resolve_relative_path (library_location, realpath);
-	g_object_unref (library_location);
-	g_free (realpath);
-
-	if (g_str_has_prefix (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE), "audio/x-raw")) {
-		GMAudioProfile *profile;
-		profile = gm_audio_profile_lookup (preferred_format);
-		if (profile)
-			extension = g_strdup (gm_audio_profile_get_extension (profile));
-	}
-
-	if (extension == NULL) {
-		const char *uri;
-		const char *loc;
-		char *tmp;
-
-		/* use the old extension. strip anything after a '?' for http/daap/etc */
-		uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
-		loc = g_utf8_strrchr (uri, -1, '.');
-		if (loc == NULL)
-			loc = g_utf8_strrchr (uri, -1, '/');
-		if (loc == NULL)
-			loc = uri;
-
-		extension = g_strdup (loc + 1);
-
-		tmp = g_utf8_strchr (extension, -1, '?');
-		if (tmp)
-			*tmp = '\0';
-	}
-
-	realfile = filepath_parse_pattern (layout_filename, entry);
-	if (extension) {
-		filename = g_strdup_printf ("%s.%s", realfile, extension);
-		g_free (realfile);
-	} else {
-		filename = realfile;
-	}
-
-	dest = g_file_resolve_relative_path (dir, filename);
-	g_object_unref (dir);
-	g_free (extension);
-	g_free (filename);
-
-	string = g_file_get_uri (dest);
-	g_object_unref (dest);
- out:
-	rb_slist_deep_free (list);
-	g_free (layout_path);
-	g_free (layout_filename);
-	g_free (preferred_format);
-
-	return string;
-}
-
 static gboolean
 impl_can_paste (RBSource *asource)
 {
-	GSList *list;
+	char *default_library_location;
 	gboolean can_paste = TRUE;
 	char *str;
 
-	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
-	can_paste = (list != NULL);
-	rb_slist_deep_free (list);
+	default_library_location = eel_gconf_get_string (CONF_DEFAULT_LIBRARY_LOCATION);
+	can_paste = (default_library_location != NULL);
+	g_free (default_library_location);
 
 	str = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH);
 	can_paste &= (str != NULL);
@@ -1243,7 +525,7 @@ impl_paste (RBSource *asource, GList *entries)
 			continue;
 		}
 
-		dest = build_filename (source, entry);
+		dest = rb_library_build_filename (source, entry);
 		if (dest == NULL) {
 			rb_debug ("could not create destination path for entry");
 			continue;
@@ -1285,69 +567,85 @@ impl_add_uri (RBSource *asource, const char *uri, const char *title, const char
 }
 
 static void
-rb_library_source_add_child_source (const char *path, RBLibrarySource *library_source)
+rb_library_source_add_child_source (RBLibrarySource *library_source, const char *path)
 {
 	RBSource *source;
-	GPtrArray *query;
 	RBShell *shell;
-	char *name;
-	GdkPixbuf *icon;
-	RhythmDBEntryType entry_type;
-	char *sort_column;
-	int sort_order;
-	GFile *file;
 
 	g_object_get (library_source,
 		      "shell", &shell,
-		      "entry-type", &entry_type,
 		      NULL);
 
-	file = g_file_new_for_uri (path);		/* ? */
-	name = g_file_get_basename (file);
-	g_object_unref (file);
-
-	rb_entry_view_get_sorting_order (rb_source_get_entry_view (RB_SOURCE (library_source)),
-					 &sort_column, &sort_order);
-
-	source = rb_auto_playlist_source_new (shell, name, FALSE);
-	query = rhythmdb_query_parse (library_source->priv->db,
-				      RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, entry_type,
-				      RHYTHMDB_QUERY_PROP_PREFIX, RHYTHMDB_PROP_LOCATION, path,
-				      RHYTHMDB_QUERY_END);
-	rb_auto_playlist_source_set_query (RB_AUTO_PLAYLIST_SOURCE (source), query,
-					   RHYTHMDB_QUERY_MODEL_LIMIT_NONE, NULL,
-					   sort_column, sort_order);
-	rhythmdb_query_free (query);
-	g_free (sort_column);
-
-	g_object_get (library_source, "icon", &icon, NULL);
-	g_object_set (source, "icon", icon, NULL);
-	if (icon != NULL) {
-		g_object_unref (icon);
-	}
+	source = rb_library_child_source_new (RB_SOURCE (library_source), path);
 
 	rb_shell_append_source (shell, source, RB_SOURCE (library_source));
 	library_source->priv->child_sources = g_list_prepend (library_source->priv->child_sources, source);
 
-	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+	rb_debug ("added child source [%p] for uri '%s'", source, path);
+
 	g_object_unref (shell);
-	g_free (name);
 }
 
 static void
+rb_library_source_remove_child_source (RBLibrarySource *library_source,
+		RBLibraryChildSource *child_source)
+{
+	char *path;
+
+	g_object_get (G_OBJECT (child_source), "path", &path, NULL);
+	rb_debug ("removing child source [%p] for uri '%s'", child_source, path);
+	g_free (path);
+
+	library_source->priv->child_sources = g_list_remove (library_source->priv->child_sources, child_source);
+	rb_source_delete_thyself (RB_SOURCE (child_source));
+}
+
+void
 rb_library_source_sync_child_sources (RBLibrarySource *source)
 {
-	GSList *list;
+	GSList *locations, *l;
+	RBLibraryChildSource *found;
+	GList *child_sources, *s;
+	char *path;
 
-	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+	locations = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+	if (g_slist_length (locations) > 1) {
+		locations = g_slist_reverse (locations);
+		for (l = locations; l != NULL; l = g_slist_next (l)) {
+			found = rb_library_source_get_child_source_for_uri (source,
+					(char *)l->data);
+			if (found == NULL) {
+				rb_library_source_add_child_source (source, (char *)l->data);
+			}
+		}
+	}
 
-	/* FIXME: don't delete and re-create sources that are still there */
-	g_list_foreach (source->priv->child_sources, (GFunc)rb_source_delete_thyself, NULL);
-	g_list_free (source->priv->child_sources);
-	source->priv->child_sources = NULL;
+	child_sources = g_list_copy (source->priv->child_sources);
+	for (s = child_sources; s != NULL; s = g_list_next (s)) {
+		g_object_get (G_OBJECT (s->data),
+				"path", &path,
+				NULL);
 
-	if (g_slist_length (list) > 1)
-		g_slist_foreach (list, (GFunc)rb_library_source_add_child_source, source);
-	rb_slist_deep_free (list);
+		g_assert (path != NULL);
+
+		if (! rb_string_slist_contains (locations, path)) {
+			rb_library_source_remove_child_source (source, RB_LIBRARY_CHILD_SOURCE (s->data));
+		}
+
+		g_free (path);
+	}
+
+	if (g_list_length (source->priv->child_sources) < 2) {
+		if (source->priv->child_sources != NULL && source->priv->child_sources->data != NULL) {
+			rb_debug ("only one child source, removing it");
+			rb_library_child_source_set_keep_songs_and_state (
+					RB_LIBRARY_CHILD_SOURCE (source->priv->child_sources->data),
+					TRUE);
+			rb_library_source_remove_child_source (source,
+					RB_LIBRARY_CHILD_SOURCE (source->priv->child_sources->data));
+		}
+	}
+	rb_slist_deep_free (locations);
+	g_list_free (child_sources);
 }
 
diff --git a/sources/rb-library-source.h b/sources/rb-library-source.h
index 920e427..9ea7eac 100644
--- a/sources/rb-library-source.h
+++ b/sources/rb-library-source.h
@@ -35,6 +35,7 @@
 #include "rb-shell.h"
 #include "rb-browser-source.h"
 #include "rhythmdb.h"
+#include "rb-library-child-source.h"
 
 G_BEGIN_DECLS
 
@@ -48,7 +49,7 @@ G_BEGIN_DECLS
 typedef struct _RBLibrarySource RBLibrarySource;
 typedef struct _RBLibrarySourceClass RBLibrarySourceClass;
 
-typedef struct RBLibrarySourcePrivate RBLibrarySourcePrivate;
+typedef struct _RBLibrarySourcePrivate RBLibrarySourcePrivate;
 
 struct _RBLibrarySource
 {
@@ -62,10 +63,40 @@ struct _RBLibrarySourceClass
 	RBBrowserSourceClass parent;
 };
 
+struct _RBLibrarySourcePrivate
+{
+	RhythmDB *db;
+
+	gboolean loading_prefs;
+	RBShellPreferences *shell_prefs;
+
+	GtkWidget *config_widget;
+
+	GList *child_sources;
+
+	GtkWidget *library_location_entry;
+	GtkWidget *layout_path_menu;
+	GtkWidget *layout_filename_menu;
+	GtkWidget *preferred_format_menu;
+	GtkWidget *layout_example_label;
+	GtkWidget *locations_tree;
+
+	guint library_location_notify_id;
+	guint default_library_location_notify_id;
+	guint ui_dir_notify_id;
+	guint layout_path_notify_id;
+	guint layout_filename_notify_id;
+	guint monitor_library_locations_notify_id;
+};
+
 GType		rb_library_source_get_type		(void);
 
 RBSource *      rb_library_source_new			(RBShell *shell);
 
+RBLibraryChildSource * rb_library_source_get_child_source_for_uri (RBLibrarySource *source, const char *uri);
+
+void rb_library_source_sync_child_sources (RBLibrarySource *source);
+
 G_END_DECLS
 
 #endif /* __RB_LIBRARY_SOURCE_H */
