Index: bindings/python/rb.defs
===================================================================
RCS file: /cvs/gnome/rhythmbox/bindings/python/rb.defs,v
retrieving revision 1.32
diff -u -p -r1.32 rb.defs
--- bindings/python/rb.defs	31 Jul 2006 11:52:12 -0000	1.32
+++ bindings/python/rb.defs	6 Oct 2006 14:50:59 -0000
@@ -111,15 +111,15 @@
   )
 )
 
-(define-enum SourceListGroup
+(define-enum SourceGroupCategory
   (in-module "RB")
-  (c-name "RBSourceListGroup")
-  (gtype-id "RB_TYPE_SOURCELIST_GROUP")
+  (c-name "RBSourceGroupCategory")
+  (gtype-id "RB_TYPE_SOURCE_GROUP_CATEGORY")
   (values
-    '("fixed" "RB_SOURCELIST_GROUP_FIXED")
-    '("persistant" "RB_SOURCELIST_GROUP_PERSISTANT")
-    '("removable" "RB_SOURCELIST_GROUP_REMOVABLE")
-    '("transient" "RB_SOURCELIST_GROUP_TRANSIENT")
+    '("fixed" "RB_SOURCE_GROUP_CATEGORY_FIXED")
+    '("persistant" "RB_SOURCE_GROUP_CATEGORY_PERSISTANT")
+    '("removable" "RB_SOURCE_GROUP_CATEGORY_REMOVABLE")
+    '("transient" "RB_SOURCE_GROUP_CATEGORY_TRANSIENT")
   )
 )
 
Index: plugins/audioscrobbler/rb-lastfm-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/plugins/audioscrobbler/rb-lastfm-source.c,v
retrieving revision 1.1
diff -u -p -r1.1 rb-lastfm-source.c
--- plugins/audioscrobbler/rb-lastfm-source.c	6 Oct 2006 13:50:58 -0000	1.1
+++ plugins/audioscrobbler/rb-lastfm-source.c	6 Oct 2006 14:50:59 -0000
@@ -569,6 +569,7 @@ rb_lastfm_source_new (RBShell *shell)
 					  "shell", shell,
 					  "entry-type", entry_type,
 					  "proxy-config", proxy_config,
+					  "source-group", RB_SOURCE_GROUP_LIBRARY,
 					  NULL));
 	rb_shell_register_entry_type_for_source (shell, source, entry_type);
 
Index: plugins/generic-player/rb-generic-player-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/plugins/generic-player/rb-generic-player-source.c,v
retrieving revision 1.19
diff -u -p -r1.19 rb-generic-player-source.c
--- plugins/generic-player/rb-generic-player-source.c	30 Aug 2006 10:03:20 -0000	1.19
+++ plugins/generic-player/rb-generic-player-source.c	6 Oct 2006 14:50:59 -0000
@@ -295,7 +295,7 @@ rb_generic_player_source_new (RBShell *s
 							 "entry-type", entry_type,
 							 "volume", volume,
 							 "shell", shell,
-							 "sourcelist-group", RB_SOURCELIST_GROUP_REMOVABLE,
+							 "source-group", RB_SOURCE_GROUP_DEVICES,
 							 NULL));
 
 	rb_generic_player_source_get_device_info (source);
Index: plugins/generic-player/rb-nokia770-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/plugins/generic-player/rb-nokia770-source.c,v
retrieving revision 1.11
diff -u -p -r1.11 rb-nokia770-source.c
--- plugins/generic-player/rb-nokia770-source.c	17 Jul 2006 14:51:41 -0000	1.11
+++ plugins/generic-player/rb-nokia770-source.c	6 Oct 2006 14:50:59 -0000
@@ -90,7 +90,7 @@ rb_nokia770_source_new (RBShell *shell, 
 						   "entry-type", entry_type,
 						   "volume", volume,
 						   "shell", shell,
-						   "sourcelist-group", RB_SOURCELIST_GROUP_REMOVABLE,
+						   "source-group", RB_SOURCE_GROUP_DEVICES,
 						   NULL));
 
 	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
Index: plugins/generic-player/rb-psp-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/plugins/generic-player/rb-psp-source.c,v
retrieving revision 1.12
diff -u -p -r1.12 rb-psp-source.c
--- plugins/generic-player/rb-psp-source.c	23 Jul 2006 16:00:17 -0000	1.12
+++ plugins/generic-player/rb-psp-source.c	6 Oct 2006 14:50:59 -0000
@@ -94,7 +94,7 @@ rb_psp_source_new (RBShell *shell, Gnome
 					  "entry-type", entry_type,
 					  "volume", volume,
 					  "shell", shell,
-					  "sourcelist-group", RB_SOURCELIST_GROUP_REMOVABLE,
+					  "source-group", RB_SOURCE_GROUP_DEVICES,
 					  NULL));
 
 	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
Index: shell/rb-playlist-manager.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-playlist-manager.c,v
retrieving revision 1.114
diff -u -p -r1.114 rb-playlist-manager.c
--- shell/rb-playlist-manager.c	31 Jul 2006 11:52:12 -0000	1.114
+++ shell/rb-playlist-manager.c	6 Oct 2006 14:51:00 -0000
@@ -422,7 +422,7 @@ rb_playlist_manager_get_property (GObjec
 {
 	RBPlaylistManager *mgr = RB_PLAYLIST_MANAGER (object);
 
-	switch (prop_id) {       
+	switch (prop_id) {
         case PROP_PLAYLIST_NAME:
                 g_value_set_string (value, mgr->priv->playlists_file);
                 break;
@@ -471,16 +471,16 @@ handle_playlist_entry_cb (TotemPlParser 
 			  RBPlaylistManager *mgr)
 {
 	char *uri;
-	
+
 	uri = rb_canonicalise_uri (uri_maybe);
 	g_return_if_fail (uri != NULL);
 
 	rb_debug ("adding uri %s (title %s, genre %s) from playlist",
 		  uri, title, genre);
-	if (!rb_shell_add_uri (mgr->priv->shell, 
-			       uri, 
-			       title, 
-			       genre, 
+	if (!rb_shell_add_uri (mgr->priv->shell,
+			       uri,
+			       title,
+			       genre,
 			       NULL))
 		return;
 
@@ -749,6 +749,38 @@ rb_playlist_manager_save_data (struct RB
 	return NULL;
 }
 
+static gboolean
+model_foreach_func (GtkTreeModel *model,
+		    GtkTreePath  *path,
+		    GtkTreeIter  *iter,
+		    xmlNodePtr    root)
+{
+	RBSource *source;
+	gboolean  local;
+
+	gtk_tree_model_get (model,
+			    iter,
+			    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source,
+			    -1);
+	if (source == NULL) {
+		goto out;
+	}
+	if (RB_IS_PLAYLIST_SOURCE (source) == FALSE) {
+		goto out;
+	}
+
+	g_object_get (source, "is-local", &local, NULL);
+	if (local) {
+		rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (source), root);
+	}
+ out:
+	if (source != NULL) {
+		g_object_unref (source);
+	}
+
+	return FALSE;
+}
+
 /**
  * rb_playlist_manager_save_playlists
  * @mgr: the #RBPlaylistManager
@@ -767,7 +799,6 @@ rb_playlist_manager_save_playlists (RBPl
 {
 	xmlNodePtr root;
 	struct RBPlaylistManagerSaveData *data;
-	GtkTreeIter iter;
 	GtkTreeModel *fmodel;
 	GtkTreeModel *model;
 
@@ -784,40 +815,16 @@ rb_playlist_manager_save_playlists (RBPl
 	data = g_new0 (struct RBPlaylistManagerSaveData, 1);
 	data->mgr = mgr;
 	data->doc = xmlNewDoc (RB_PLAYLIST_MGR_VERSION);
-	g_object_ref (G_OBJECT (mgr));
+	g_object_ref (mgr);
 
 	root = xmlNewDocNode (data->doc, NULL, RB_PLAYLIST_MGR_PL, NULL);
 	xmlDocSetRootElement (data->doc, root);
 
-	g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
+	g_object_get (mgr->priv->sourcelist, "model", &fmodel, NULL);
 	model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
 	g_object_unref (fmodel);
 
-	if (gtk_tree_model_get_iter_first (model, &iter)) {
-		do {
-			RBSource *source;
-			gboolean local;
-
-			gtk_tree_model_get (model,
-					    &iter,
-					    RB_SOURCELIST_MODEL_COLUMN_SOURCE,
-					    &source,
-					    -1);
-			if (source == NULL) {
-				continue;
-			}
-			if (RB_IS_PLAYLIST_SOURCE (source) == FALSE) {
-				g_object_unref (source);
-				continue;
-			}
-
-			g_object_get (G_OBJECT (source), "is-local", &local, NULL);
-			if (local)
-				rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (source), root);
-
-			g_object_unref (source);
-		} while (gtk_tree_model_iter_next (model, &iter));
-	}
+	gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc)model_foreach_func, root);
 
 	/* mark clean here.  if the save fails, we'll mark it dirty again */
 	rb_playlist_manager_set_dirty (data->mgr, FALSE);
Index: shell/rb-shell.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-shell.c,v
retrieving revision 1.476
diff -u -p -r1.476 rb-shell.c
--- shell/rb-shell.c	2 Oct 2006 14:04:20 -0000	1.476
+++ shell/rb-shell.c	6 Oct 2006 14:51:01 -0000
@@ -1302,6 +1302,7 @@ rb_shell_constructor (GType type,
 					     shell);
 
 	construct_db (shell);
+	rb_source_group_init ();
 
 	/* initialize shell services */
 	construct_widgets (shell);
Index: shell/rb-source-header.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-source-header.c,v
retrieving revision 1.54
diff -u -p -r1.54 rb-source-header.c
--- shell/rb-source-header.c	14 Jul 2006 12:30:53 -0000	1.54
+++ shell/rb-source-header.c	6 Oct 2006 14:51:01 -0000
@@ -229,7 +229,6 @@ rb_source_header_init (RBSourceHeader *h
 	header->priv->tooltips = gtk_tooltips_new ();
 	gtk_tooltips_enable (header->priv->tooltips);
 
-	gtk_table_set_homogeneous (GTK_TABLE (header), TRUE);
 	gtk_table_set_col_spacings (GTK_TABLE (header), 5);
 	gtk_table_resize (GTK_TABLE (header), 1, 3);
 
Index: sources/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/Makefile.am,v
retrieving revision 1.45
diff -u -p -r1.45 Makefile.am
--- sources/Makefile.am	2 Oct 2006 11:55:03 -0000	1.45
+++ sources/Makefile.am	6 Oct 2006 14:51:01 -0000
@@ -1,7 +1,11 @@
 ## arch-tag: Automake rules for source object library
 noinst_LTLIBRARIES = libsources.la libsourcesimpl.la
 
-libsources_la_SOURCES = rb-source.c rb-source.h
+libsources_la_SOURCES = 		\
+	rb-source.c			\
+	rb-source.h			\
+	rb-source-group.c		\
+	rb-source-group.h
 
 libsourcesimpl_la_SOURCES =		\
 	rb-sourcelist.c			\
Index: sources/rb-audiocd-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-audiocd-source.c,v
retrieving revision 1.46
diff -u -p -r1.46 rb-audiocd-source.c
--- sources/rb-audiocd-source.c	2 Oct 2006 11:55:03 -0000	1.46
+++ sources/rb-audiocd-source.c	6 Oct 2006 14:51:01 -0000
@@ -178,7 +178,7 @@ rb_audiocd_source_new (RBShell *shell,
 			       "volume", volume,
 			       "shell", shell,
 			       "sorting-key", NULL,
-			       "sourcelist-group", RB_SOURCELIST_GROUP_REMOVABLE,
+			       "source-group", RB_SOURCE_GROUP_DEVICES,
 			       NULL);
 
 	g_free (device_path);
Index: sources/rb-auto-playlist-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-auto-playlist-source.c,v
retrieving revision 1.29
diff -u -p -r1.29 rb-auto-playlist-source.c
--- sources/rb-auto-playlist-source.c	14 Aug 2006 12:43:34 -0000	1.29
+++ sources/rb-auto-playlist-source.c	6 Oct 2006 14:51:02 -0000
@@ -252,7 +252,7 @@ rb_auto_playlist_source_new (RBShell *sh
 					"shell", shell,
 					"is-local", local,
 					"entry-type", RHYTHMDB_ENTRY_TYPE_SONG,
-					"sourcelist-group", RB_SOURCELIST_GROUP_PERSISTANT,
+					"source-group", RB_SOURCE_GROUP_PLAYLISTS,
 					NULL));
 }
 
Index: sources/rb-daap-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-daap-source.c,v
retrieving revision 1.46
diff -u -p -r1.46 rb-daap-source.c
--- sources/rb-daap-source.c	2 Oct 2006 11:55:03 -0000	1.46
+++ sources/rb-daap-source.c	6 Oct 2006 14:51:02 -0000
@@ -333,7 +333,7 @@ rb_daap_source_new (RBShell *shell,
 					  "visibility", TRUE,
 					  "sorting-key", CONF_STATE_SORTING,
 					  "password-protected", password_protected,
-					  "sourcelist-group", RB_SOURCELIST_GROUP_TRANSIENT,
+					  "source-group", RB_SOURCE_GROUP_SHARED,
 					  NULL));
 
 	if (icon != NULL) {
Index: sources/rb-ipod-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-ipod-source.c,v
retrieving revision 1.65
diff -u -p -r1.65 rb-ipod-source.c
--- sources/rb-ipod-source.c	2 Oct 2006 22:09:37 -0000	1.65
+++ sources/rb-ipod-source.c	6 Oct 2006 14:51:03 -0000
@@ -213,11 +213,11 @@ rb_ipod_source_new (RBShell *shell,
 	g_object_unref (db);
 
 	source = RB_IPOD_SOURCE (g_object_new (RB_TYPE_IPOD_SOURCE,
-					  "entry-type", entry_type,
-					  "volume", volume,
-					  "shell", shell,
-					  "sourcelist-group", RB_SOURCELIST_GROUP_REMOVABLE,
-					  NULL));
+                                               "entry-type", entry_type,
+                                               "volume", volume,
+                                               "shell", shell,
+                                               "source-group", RB_SOURCE_GROUP_DEVICES,
+                                               NULL));
 
 	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
 
Index: sources/rb-iradio-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-iradio-source.c,v
retrieving revision 1.132
diff -u -p -r1.132 rb-iradio-source.c
--- sources/rb-iradio-source.c	3 Oct 2006 09:20:26 -0000	1.132
+++ sources/rb-iradio-source.c	6 Oct 2006 14:51:03 -0000
@@ -482,6 +482,7 @@ rb_iradio_source_new (RBShell *shell)
 					  "name", _("Radio"),
 					  "shell", shell,
 					  "entry-type", entry_type,
+					  "source-group", RB_SOURCE_GROUP_LIBRARY,
 					  NULL));
 	rb_shell_register_entry_type_for_source (shell, source, entry_type);
 	return source;
Index: sources/rb-library-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-library-source.c,v
retrieving revision 1.184
diff -u -p -r1.184 rb-library-source.c
--- sources/rb-library-source.c	14 Aug 2006 09:03:24 -0000	1.184
+++ sources/rb-library-source.c	6 Oct 2006 14:51:03 -0000
@@ -312,8 +312,9 @@ rb_library_source_new (RBShell *shell)
 					 size,
 					 0, NULL);
 	source = RB_SOURCE (g_object_new (RB_TYPE_LIBRARY_SOURCE,
-					  "name", _("Library"),
+					  "name", _("Music"),
 					  "entry-type", entry_type,
+					  "source-group", RB_SOURCE_GROUP_LIBRARY,
 					  "sorting-key", CONF_STATE_LIBRARY_SORTING,
 					  "shell", shell,
 					  "icon", icon,
Index: sources/rb-play-queue-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-play-queue-source.c,v
retrieving revision 1.20
diff -u -p -r1.20 rb-play-queue-source.c
--- sources/rb-play-queue-source.c	14 Aug 2006 09:03:25 -0000	1.20
+++ sources/rb-play-queue-source.c	6 Oct 2006 14:51:03 -0000
@@ -244,6 +244,7 @@ rb_play_queue_source_new (RBShell *shell
 					"shell", shell,
 					"is-local", TRUE,
 					"entry-type", RHYTHMDB_ENTRY_TYPE_INVALID,
+					"source-group", RB_SOURCE_GROUP_LIBRARY,
 					NULL));
 }
 
Index: sources/rb-podcast-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-podcast-source.c,v
retrieving revision 1.79
diff -u -p -r1.79 rb-podcast-source.c
--- sources/rb-podcast-source.c	18 Sep 2006 12:27:35 -0000	1.79
+++ sources/rb-podcast-source.c	6 Oct 2006 14:51:04 -0000
@@ -812,6 +812,7 @@ rb_podcast_source_new (RBShell *shell)
 					  "name", _("Podcasts"),
 					  "shell", shell,
 					  "entry-type", RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
+					  "source-group", RB_SOURCE_GROUP_LIBRARY,
 					  NULL));
 
 	rb_shell_register_entry_type_for_source (shell, source,
Index: sources/rb-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-source.c,v
retrieving revision 1.82
diff -u -p -r1.82 rb-source.c
--- sources/rb-source.c	23 Aug 2006 22:29:54 -0000	1.82
+++ sources/rb-source.c	6 Oct 2006 14:51:04 -0000
@@ -36,6 +36,7 @@
 #include "rb-source.h"
 #include "rb-util.h"
 #include "rb-static-playlist-source.h"
+#include "rb-source-group.h"
 
 static void rb_source_class_init (RBSourceClass *klass);
 static void rb_source_init (RBSource *source);
@@ -82,17 +83,17 @@ G_DEFINE_ABSTRACT_TYPE (RBSource, rb_sou
 
 struct _RBSourcePrivate
 {
-	char *name;
+	char               *name;
 
-	RBShell *shell;
-	gboolean visible;
+	RBShell            *shell;
+	gboolean            visible;
 	RhythmDBQueryModel *query_model;
-	GdkPixbuf *pixbuf;
-	RBSourceListGroup sourcelist_group;
-	guint hidden_when_empty : 1;
-	guint update_visibility_id;
-	guint update_status_id;
-	RhythmDBEntryType entry_type;
+	GdkPixbuf          *pixbuf;
+	guint               hidden_when_empty : 1;
+	guint               update_visibility_id;
+	guint               update_status_id;
+	RhythmDBEntryType   entry_type;
+	RBSourceGroup      *source_group;
 };
 
 enum
@@ -105,7 +106,7 @@ enum
 	PROP_VISIBLE,
 	PROP_QUERY_MODEL,
 	PROP_HIDDEN_WHEN_EMPTY,
-	PROP_SOURCELIST_GROUP,
+	PROP_SOURCE_GROUP,
 	PROP_ENTRY_TYPE
 };
 
@@ -221,14 +222,12 @@ rb_source_class_init (RBSourceClass *kla
 							      RHYTHMDB_TYPE_QUERY_MODEL,
 							      G_PARAM_READWRITE));
 	g_object_class_install_property (object_class,
-					 PROP_SOURCELIST_GROUP,
-					 g_param_spec_enum ("sourcelist-group",
-						 	    "sourcelist group",
-							    "sourcelist group",
-							    RB_TYPE_SOURCELIST_GROUP,
-							    RB_SOURCELIST_GROUP_FIXED,
-							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
+					 PROP_SOURCE_GROUP,
+					 g_param_spec_boxed ("source-group",
+							     "Source group",
+							     "Source group",
+							     RB_TYPE_SOURCE_GROUP,
+							     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (object_class,
 					 PROP_ENTRY_TYPE,
 					 g_param_spec_boxed ("entry-type",
@@ -469,8 +468,8 @@ rb_source_set_property (GObject *object,
 	case PROP_QUERY_MODEL:
 		rb_source_set_query_model_internal (source, g_value_get_object (value));
 		break;
-	case PROP_SOURCELIST_GROUP:
-		priv->sourcelist_group = g_value_get_enum (value);
+	case PROP_SOURCE_GROUP:
+		priv->source_group = g_value_get_boxed (value);
 		break;
 	case PROP_ENTRY_TYPE:
 		priv->entry_type = g_value_get_boxed (value);
@@ -515,8 +514,8 @@ rb_source_get_property (GObject *object,
 	case PROP_QUERY_MODEL:
 		g_value_set_object (value, priv->query_model);
 		break;
-	case PROP_SOURCELIST_GROUP:
-		g_value_set_enum (value, priv->sourcelist_group);
+	case PROP_SOURCE_GROUP:
+		g_value_set_boxed (value, priv->source_group);
 		break;
 	case PROP_ENTRY_TYPE:
 		g_value_set_boxed (value, priv->entry_type);
@@ -1210,26 +1209,6 @@ rb_source_eof_type_get_type (void)
 		};
 
 		etype = g_enum_register_static ("RBSourceEOFType", values);
-	}
-
-	return etype;
-}
-
-GType
-rb_sourcelist_group_get_type (void)
-{
-	static GType etype = 0;
-
-	if (etype == 0) {
-		static const GEnumValue values[] = {
-			ENUM_ENTRY (RB_SOURCELIST_GROUP_FIXED, "Fixed single instance source"),
-			ENUM_ENTRY (RB_SOURCELIST_GROUP_PERSISTANT, "Persistant multiple-instance source"),
-			ENUM_ENTRY (RB_SOURCELIST_GROUP_REMOVABLE, "Source representing a removable device"),
-			ENUM_ENTRY (RB_SOURCELIST_GROUP_TRANSIENT, "Transient source (eg. network shares)"),
-			{ 0, 0, 0 }
-		};
-
-		etype = g_enum_register_static ("RBSourcelistGroupType", values);
 	}
 
 	return etype;
Index: sources/rb-source.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-source.h,v
retrieving revision 1.49
diff -u -p -r1.49 rb-source.h
--- sources/rb-source.h	31 Jul 2006 11:52:14 -0000	1.49
+++ sources/rb-source.h	6 Oct 2006 14:51:04 -0000
@@ -26,6 +26,7 @@
 #include <gtk/gtkhbox.h>
 #include <gtk/gtkactiongroup.h>
 
+#include "rb-source-group.h"
 #include "rb-entry-view.h"
 #include "rb-shell-preferences.h"
 
@@ -38,21 +39,8 @@ typedef enum {
 	RB_SOURCE_EOF_NEXT,
 } RBSourceEOFType;
 
-/* groupings in the source list */
-typedef enum {
-	RB_SOURCELIST_GROUP_FIXED,	/* library, iradio, podcast, queue */
-	RB_SOURCELIST_GROUP_PERSISTANT,	/* playlists */
-	RB_SOURCELIST_GROUP_REMOVABLE,	/* ipod, generic audio player, audio CD */
-	RB_SOURCELIST_GROUP_TRANSIENT,	/* DAAP shares */
-
-	RB_SOURCELIST_GROUP_LAST
-} RBSourceListGroup;
-
 GType rb_source_eof_type_get_type (void);
 #define RB_TYPE_SOURCE_EOF_TYPE	(rb_source_eof_type_get_type())
-
-GType rb_sourcelist_group_get_type (void);
-#define RB_TYPE_SOURCELIST_GROUP (rb_sourcelist_group_get_type())
 
 #define RB_TYPE_SOURCE         (rb_source_get_type ())
 #define RB_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_SOURCE, RBSource))
Index: sources/rb-sourcelist-model.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-sourcelist-model.c,v
retrieving revision 1.32
diff -u -p -r1.32 rb-sourcelist-model.c
--- sources/rb-sourcelist-model.c	2 Sep 2006 00:54:18 -0000	1.32
+++ sources/rb-sourcelist-model.c	6 Oct 2006 14:51:04 -0000
@@ -37,7 +37,6 @@
 
 struct RBSourceListModelPrivate
 {
-	GtkTreeRowReference *groups[RB_SOURCELIST_GROUP_LAST];
 };
 
 #define RB_SOURCELIST_MODEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SOURCELIST_MODEL, RBSourceListModelPrivate))
@@ -190,15 +189,10 @@ static void
 rb_sourcelist_model_finalize (GObject *object)
 {
 	RBSourceListModel *model;
-	int i;
 
 	g_return_if_fail (RB_IS_SOURCELIST_MODEL (object));
 	model = RB_SOURCELIST_MODEL (object);
 
-	for (i = 0; i < RB_SOURCELIST_GROUP_LAST; i++) {
-		gtk_tree_row_reference_free (model->priv->groups[i]);
-	}
-
 	G_OBJECT_CLASS (rb_sourcelist_model_parent_class)->finalize (object);
 }
 
@@ -207,7 +201,6 @@ rb_sourcelist_model_new (void)
 {
 	RBSourceListModel *model;
 	GtkTreeStore *store;
-	int i;
  	GType *column_types = g_new (GType, RB_SOURCELIST_MODEL_N_COLUMNS);
 
 	column_types[RB_SOURCELIST_MODEL_COLUMN_PLAYING] = G_TYPE_BOOLEAN;
@@ -216,6 +209,8 @@ rb_sourcelist_model_new (void)
 	column_types[RB_SOURCELIST_MODEL_COLUMN_SOURCE] = G_TYPE_OBJECT;
 	column_types[RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES] = PANGO_TYPE_ATTR_LIST;
 	column_types[RB_SOURCELIST_MODEL_COLUMN_VISIBILITY] = G_TYPE_BOOLEAN;
+	column_types[RB_SOURCELIST_MODEL_COLUMN_IS_GROUP] = G_TYPE_BOOLEAN;
+	column_types[RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY] = RB_TYPE_SOURCE_GROUP_CATEGORY;
 	store = gtk_tree_store_newv (RB_SOURCELIST_MODEL_N_COLUMNS,
 				     column_types);
 
@@ -225,23 +220,6 @@ rb_sourcelist_model_new (void)
 						   NULL));
 	g_object_unref (store);
 
-	/* create marker rows used to separate source groups */
-	for (i = 0; i < RB_SOURCELIST_GROUP_LAST; i++) {
-		GtkTreeIter iter;
-		GtkTreePath *path;
-
-		gtk_tree_store_append (store, &iter, NULL);
-		gtk_tree_store_set (store, &iter,
-				    RB_SOURCELIST_MODEL_COLUMN_NAME, "",
-				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, NULL,
-				    RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, FALSE,
-				    -1);
-
-		path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
-		model->priv->groups[i] = gtk_tree_row_reference_new (GTK_TREE_MODEL (store), path);
-		gtk_tree_path_free (path);
-	}
-
 	/* ensure the group markers get updated as sources are added and removed */
 	g_signal_connect_object (G_OBJECT (store), "row-inserted",
 				 G_CALLBACK (rb_sourcelist_model_row_inserted_cb),
@@ -260,48 +238,6 @@ rb_sourcelist_model_new (void)
 }
 
 static gboolean
-real_row_is_separator (RBSourceListModel *model, GtkTreeIter *iter)
-{
-	int i;
-	GtkTreePath *path;
-	GtkTreeModel *real_model;
-	GtkTreePath *group_path;
-
-	real_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
-	path = gtk_tree_model_get_path (real_model, iter);
-
-	/* -1 here because the last group marker is always the last row in
-	 * the model, and therefore can never be shown.
-	 */
-	for (i = 0; i < RB_SOURCELIST_GROUP_LAST-1; i++) {
-		group_path = rb_sourcelist_model_get_group_path (model, i);
-
-		if (gtk_tree_path_compare (path, group_path) == 0) {
-			/* okay, we know this is a group marker.
-			 * if the next row is not a group marker,
-			 * then we should show this row as a separator.
-			 */
-			gboolean separator = FALSE;
-
-			gtk_tree_path_next (path);
-
-			gtk_tree_path_free (group_path);
-			group_path = rb_sourcelist_model_get_group_path (model, i+1);
-			separator = (gtk_tree_path_compare (path, group_path) != 0);
-
-			gtk_tree_path_free (group_path);
-			gtk_tree_path_free (path);
-			return separator;
-		}
-		gtk_tree_path_free (group_path);
-	}
-
-	gtk_tree_path_free (path);
-
-	return FALSE;
-}
-
-static gboolean
 rb_sourcelist_model_is_row_visible (GtkTreeModel *model,
 				    GtkTreeIter *iter,
 				    RBSourceListModel *sourcelist)
@@ -318,52 +254,32 @@ rb_sourcelist_model_is_row_visible (GtkT
 		g_object_unref (source);
 
 		return visible;
-	} else {
-		return real_row_is_separator (sourcelist, iter);
-	}
-}
-
-gboolean
-rb_sourcelist_model_row_is_separator (GtkTreeModel *model,
-				      GtkTreeIter *iter,
-				      RBSourceListModel *sourcelist)
-{
-	GtkTreeIter real_iter;
-	RBSource *source;
-
-	/* rows with actual sources are never separators */
-	gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
-			    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
-	if (source != NULL) {
-		g_object_unref (source);
-		return FALSE;
 	}
 
-	gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (sourcelist),
-							  &real_iter,
-							  iter);
-	return real_row_is_separator (sourcelist, &real_iter);
+	return TRUE;
 }
 
-static int
-get_group_for_path (RBSourceListModel *model, GtkTreePath *path)
+static RBSourceGroup *
+get_group_for_path (RBSourceListModel *model,
+		    GtkTreePath       *path)
 {
-	GtkTreePath *group_path;
-	gboolean found = FALSE;
-	int i;
+	RBSource      *source;
+	RBSourceGroup *group;
+	GtkTreeIter    iter;
+
+	group = NULL;
 
-	for (i=0; i < RB_SOURCELIST_GROUP_LAST; i++) {
-		group_path = rb_sourcelist_model_get_group_path (model, i);
-		g_assert (group_path);
+	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) {
 
-		found = (gtk_tree_path_compare (group_path, path) == 1);
-		gtk_tree_path_free (group_path);
+		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
+				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source,
+				    -1);
 
-		if (found)
-			return i;
+		g_object_get (source, "source-group", &group, NULL);
+		g_object_unref (source);
 	}
 
-	g_assert_not_reached ();
+	return group;
 }
 
 static gboolean
@@ -408,18 +324,19 @@ rb_sourcelist_model_drag_data_received (
         }
 
 	if (selection_data->type == gdk_atom_intern ("application/x-rhythmbox-source", TRUE)) {
-		GtkTreePath *path;
-		GtkTreePath *real_dest;
-		char *path_str;
-		GtkTreeIter iter, real_iter;
-		GtkTreeIter real_dest_iter;
+		GtkTreePath  *path;
+		GtkTreePath  *real_dest;
+		char         *path_str;
+		GtkTreeIter   iter, real_iter;
+		GtkTreeIter   real_dest_iter;
 		GtkTreeModel *real_model;
-		RBSource *source;
-		RBSourceListGroup group;
-		int dest_group;
+		RBSource      *source;
+		RBSourceGroup *group;
+		RBSourceGroup *dest_group;
 
-		if (!dest)
+		if (! dest) {
 			return FALSE;
+		}
 
 		real_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
 
@@ -431,37 +348,36 @@ rb_sourcelist_model_drag_data_received (
 		gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
 								  &real_iter, &iter);
 
-		real_dest =
-			gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model),
-									  dest);
+		real_dest = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model),
+									      dest);
 
 		gtk_tree_model_get (GTK_TREE_MODEL (real_model), &real_iter,
 				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source,
 				    -1);
-		g_object_get (source, "sourcelist-group", &group, NULL);
+		g_object_get (source, "source-group", &group, NULL);
 		g_object_unref (source);
 
 		/* restrict sources to within their group */
 		dest_group = get_group_for_path (model, real_dest);
-		if (dest_group < group) {
-			gtk_tree_path_free (real_dest);
-			real_dest = rb_sourcelist_model_get_group_path (model, group-1);
-			pos = GTK_TREE_VIEW_DROP_AFTER;
-		} else if (dest_group > group) {
-			gtk_tree_path_free (real_dest);
-			real_dest = rb_sourcelist_model_get_group_path (model, group);
-			pos = GTK_TREE_VIEW_DROP_BEFORE;
+		if (dest_group == NULL
+		    || group == NULL
+		    || strcmp (dest_group->name, group->name) != 0) {
+			/* bail */
+			return FALSE;
 		}
 
+
+		/* FIXME: this won't work with a sorted model */
 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (real_model),
 					     &real_dest_iter, real_dest)) {
 
-			if (pos == GTK_TREE_VIEW_DROP_AFTER)
+			if (pos == GTK_TREE_VIEW_DROP_AFTER) {
 				gtk_tree_store_move_after (GTK_TREE_STORE (real_model),
 							   &real_iter, &real_dest_iter);
-			else
+			} else {
 				gtk_tree_store_move_before (GTK_TREE_STORE (real_model),
 							    &real_iter, &real_dest_iter);
+			}
 
 		} else {
 			gtk_tree_store_move_before (GTK_TREE_STORE (real_model),
@@ -527,8 +443,8 @@ path_is_reorderable (RBSourceListModel *
 	GtkTreeIter iter;
 
 	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, dest)) {
-		RBSource *source;
-		RBSourceListGroup group;
+		RBSource      *source;
+		RBSourceGroup *group;
 
 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
 				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
@@ -536,12 +452,12 @@ path_is_reorderable (RBSourceListModel *
 		if (source == NULL)
 			return FALSE;
 
-		g_object_get (source, "sourcelist-group", &group, NULL);
+		g_object_get (source, "source-group", &group, NULL);
 		g_object_unref (source);
 
 		/* fixed and transient sources are not reorderable, everything else is */
-		return (group != RB_SOURCELIST_GROUP_FIXED &&
-			group != RB_SOURCELIST_GROUP_TRANSIENT);
+		return (group->category != RB_SOURCE_GROUP_CATEGORY_FIXED &&
+			group->category != RB_SOURCE_GROUP_CATEGORY_TRANSIENT);
 	}
 
 	return FALSE;
@@ -634,17 +550,20 @@ rb_sourcelist_model_row_draggable (RbTre
 	path = gtk_tree_row_reference_get_path (path_list->data);
 
 	if (path && gtk_tree_model_get_iter (model, &iter, path)) {
-		RBSource *source;
-		RBSourceListGroup group;
+		RBSource      *source;
+		RBSourceGroup *group;
+		gboolean       can;
 
 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
 				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
 
-		g_object_get (source, "sourcelist-group", &group, NULL);
+		g_object_get (source, "source-group", &group, NULL);
 		g_object_unref (source);
 
-		return (group != RB_SOURCELIST_GROUP_FIXED &&
-			group != RB_SOURCELIST_GROUP_TRANSIENT);
+		can = (group->category != RB_SOURCE_GROUP_CATEGORY_FIXED &&
+		       group->category != RB_SOURCE_GROUP_CATEGORY_TRANSIENT);
+
+		return can;
 	}
 
 	return FALSE;
@@ -754,11 +673,4 @@ rb_sourcelist_model_row_deleted_cb (GtkT
 				    RBSourceListModel *sourcelist)
 {
 	gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sourcelist));
-}
-
-GtkTreePath *
-rb_sourcelist_model_get_group_path (RBSourceListModel *sourcelist,
-				    RBSourceListGroup group)
-{
-	return gtk_tree_row_reference_get_path (sourcelist->priv->groups[group]);
 }
Index: sources/rb-sourcelist-model.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-sourcelist-model.h,v
retrieving revision 1.9
diff -u -p -r1.9 rb-sourcelist-model.h
--- sources/rb-sourcelist-model.h	4 Jun 2006 10:19:22 -0000	1.9
+++ sources/rb-sourcelist-model.h	6 Oct 2006 14:51:04 -0000
@@ -61,6 +61,8 @@ enum
 	RB_SOURCELIST_MODEL_COLUMN_SOURCE,
 	RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES,
 	RB_SOURCELIST_MODEL_COLUMN_VISIBILITY,
+	RB_SOURCELIST_MODEL_COLUMN_IS_GROUP,
+	RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY,
 	RB_SOURCELIST_MODEL_N_COLUMNS
 };
 
@@ -70,11 +72,6 @@ GtkTreeModel *	rb_sourcelist_model_new		
 
 void		rb_sourcelist_model_set_dnd_targets (RBSourceListModel *sourcelist,
 						     GtkTreeView *treeview);
-GtkTreePath *	rb_sourcelist_model_get_group_path (RBSourceListModel *sourcelist,
-						    RBSourceListGroup group);
-gboolean	rb_sourcelist_model_row_is_separator (GtkTreeModel *model,
-						      GtkTreeIter *iter,
-						      RBSourceListModel *sourcelist);
 
 G_END_DECLS
 
Index: sources/rb-sourcelist.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-sourcelist.c,v
retrieving revision 1.43
diff -u -p -r1.43 rb-sourcelist.c
--- sources/rb-sourcelist.c	13 Sep 2006 22:21:47 -0000	1.43
+++ sources/rb-sourcelist.c	6 Oct 2006 14:51:04 -0000
@@ -51,7 +51,6 @@ struct RBSourceListPrivate
 
 	RBSource *playing_source;
 	int child_source_count;
-	GtkTreeViewColumn *hidden_column;
 	GtkTreeViewColumn *main_column;
 
 	RBShell *shell;
@@ -113,7 +112,6 @@ static void source_name_edited_cb (GtkCe
 static void rb_sourcelist_title_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 						GtkTreeModel *tree_model, GtkTreeIter *iter,
 						RBSourceList *sourcelist);
-static void rb_sourcelist_update_expander_visibility (RBSourceList *sourcelist);
 
 static guint rb_sourcelist_signals[LAST_SIGNAL] = { 0 };
 
@@ -195,6 +193,81 @@ rb_sourcelist_class_init (RBSourceListCl
 }
 
 static void
+sourcelist_pixbuf_cell_data_func (GtkTreeViewColumn *tree_column,
+				  GtkCellRenderer   *cell,
+				  GtkTreeModel      *model,
+				  GtkTreeIter       *iter,
+				  RBSourceList      *sourcelist)
+{
+	GdkPixbuf *pixbuf;
+	gboolean   is_group;
+
+	gtk_tree_model_get (model, iter,
+			    RB_SOURCELIST_MODEL_COLUMN_IS_GROUP, &is_group,
+			    RB_SOURCELIST_MODEL_COLUMN_PIXBUF, &pixbuf,
+			    -1);
+
+	g_object_set (cell,
+		      "visible", !is_group,
+		      "pixbuf", pixbuf,
+		      NULL);
+
+	if (pixbuf != NULL) {
+		g_object_unref (pixbuf);
+	}
+
+	/*sourcelist_cell_set_background (list, cell, is_group, is_active);*/
+}
+
+static int
+compare_rows (GtkTreeModel *model,
+	      GtkTreeIter  *a,
+	      GtkTreeIter  *b,
+	      gpointer      user_data)
+{
+	char *a_name;
+	char *b_name;
+	gboolean a_is_group;
+	gboolean b_is_group;
+	RBSourceGroupCategory a_category;
+	RBSourceGroupCategory b_category;
+	int ret;
+
+	gtk_tree_model_get (model, a,
+			    RB_SOURCELIST_MODEL_COLUMN_NAME, &a_name,
+			    RB_SOURCELIST_MODEL_COLUMN_IS_GROUP, &a_is_group,
+			    RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY, &a_category,
+			    -1);
+	gtk_tree_model_get (model, b,
+			    RB_SOURCELIST_MODEL_COLUMN_NAME, &b_name,
+			    RB_SOURCELIST_MODEL_COLUMN_IS_GROUP, &b_is_group,
+			    RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY, &b_category,
+			    -1);
+
+	if (a_is_group && a_is_group) {
+		if (a_category < b_category) {
+			ret = -1;
+		} else if (a_category > b_category) {
+			ret = 1;
+		} else {
+			ret = 0;
+		}
+	} else {
+		/* sort by name */
+		if (a_category == RB_SOURCE_GROUP_CATEGORY_TRANSIENT) {
+			ret = g_utf8_collate (a_name, b_name);
+		} else {
+			ret = -1;
+		}
+	}
+
+	g_free (a_name);
+	g_free (b_name);
+
+	return ret;
+}
+
+static void
 rb_sourcelist_init (RBSourceList *sourcelist)
 {
 	GtkCellRenderer *renderer;
@@ -203,61 +276,72 @@ rb_sourcelist_init (RBSourceList *source
 
 	sourcelist->priv->filter_model = rb_sourcelist_model_new ();
 	sourcelist->priv->real_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (sourcelist->priv->filter_model));
-
-	g_signal_connect_object (G_OBJECT (sourcelist->priv->filter_model),
+	g_signal_connect_object (sourcelist->priv->filter_model,
 				 "drop_received",
 				 G_CALLBACK (drop_received_cb),
 				 sourcelist, 0);
 
 	sourcelist->priv->treeview = gtk_tree_view_new_with_model (sourcelist->priv->filter_model);
+	g_object_set (sourcelist->priv->treeview,
+		      "headers-visible", TRUE,
+		      "show-expanders", TRUE,
+		      NULL);
+
 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (sourcelist->priv->treeview), FALSE);
 	rb_sourcelist_model_set_dnd_targets (RB_SOURCELIST_MODEL (sourcelist->priv->filter_model),
 					     GTK_TREE_VIEW (sourcelist->priv->treeview));
-	gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (sourcelist->priv->treeview),
-					      (GtkTreeViewRowSeparatorFunc) rb_sourcelist_model_row_is_separator,
-					      sourcelist->priv->filter_model, NULL);
 
-	g_signal_connect_object (G_OBJECT (sourcelist->priv->treeview),
+	g_signal_connect_object (sourcelist->priv->treeview,
 				 "row_activated",
 				 G_CALLBACK (row_activated_cb),
 				 sourcelist, 0);
 
-	g_signal_connect_object (G_OBJECT (sourcelist->priv->treeview),
+	g_signal_connect_object (sourcelist->priv->treeview,
 				 "button_press_event",
 				 G_CALLBACK (button_press_cb),
 				 sourcelist, 0);
-	g_signal_connect_object (G_OBJECT (sourcelist->priv->treeview),
+	g_signal_connect_object (sourcelist->priv->treeview,
 				 "key_release_event",
 				 G_CALLBACK (key_release_cb),
 				 sourcelist, 0);
 
-	g_signal_connect_object (G_OBJECT (sourcelist->priv->treeview),
+	g_signal_connect_object (sourcelist->priv->treeview,
 				 "popup_menu",
 				 G_CALLBACK (popup_menu_cb),
 				 sourcelist, 0);
 
-	sourcelist->priv->hidden_column = gtk_tree_view_column_new ();
-	gtk_tree_view_append_column (GTK_TREE_VIEW (sourcelist->priv->treeview),
-				     sourcelist->priv->hidden_column);
-	gtk_tree_view_column_set_visible (sourcelist->priv->hidden_column, FALSE);
-	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (sourcelist->priv->treeview),
-				     sourcelist->priv->hidden_column);
-
 	sourcelist->priv->main_column = gtk_tree_view_column_new ();
 	gtk_tree_view_column_set_title (sourcelist->priv->main_column, _("S_ource"));
 	gtk_tree_view_column_set_clickable (sourcelist->priv->main_column, FALSE);
+
+        gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sourcelist->priv->real_model),
+                                         RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY,
+					 compare_rows,
+                                         NULL, NULL);
+        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sourcelist->priv->real_model),
+                                              RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY,
+                                              GTK_SORT_ASCENDING);
+
 	gtk_tree_view_append_column (GTK_TREE_VIEW (sourcelist->priv->treeview), sourcelist->priv->main_column);
 
 	/* Set up the pixbuf column */
 	renderer = gtk_cell_renderer_pixbuf_new ();
 	gtk_tree_view_column_pack_start (sourcelist->priv->main_column, renderer, FALSE);
-	gtk_tree_view_column_set_attributes (sourcelist->priv->main_column, renderer,
-				             "pixbuf", RB_SOURCELIST_MODEL_COLUMN_PIXBUF,
-					     NULL);
+	gtk_tree_view_column_set_cell_data_func (sourcelist->priv->main_column,
+						 renderer,
+						 (GtkTreeCellDataFunc) sourcelist_pixbuf_cell_data_func,
+						 sourcelist, NULL);
+
+	g_object_set (renderer,
+		      "xpad", 8,
+		      "ypad", 1,
+		      "visible", FALSE,
+		      NULL);
+
 
 	/* Set up the name column */
 	sourcelist->priv->title_renderer = renderer = gtk_cell_renderer_text_new ();
-	g_object_set (G_OBJECT (sourcelist->priv->title_renderer),
+	g_object_set (sourcelist->priv->title_renderer,
 		      "ellipsize", PANGO_ELLIPSIZE_END,
 		      NULL);
 	gtk_tree_view_column_pack_start (sourcelist->priv->main_column, renderer, TRUE);
@@ -268,13 +352,11 @@ rb_sourcelist_init (RBSourceList *source
 	g_signal_connect_object (renderer, "edited", G_CALLBACK (source_name_edited_cb), sourcelist, 0);
 
 	sourcelist->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sourcelist->priv->treeview));
-	g_signal_connect_object (G_OBJECT (sourcelist->priv->selection),
+	g_signal_connect_object (sourcelist->priv->selection,
 			         "changed",
 			         G_CALLBACK (rb_sourcelist_selection_changed_cb),
 			         sourcelist,
 				 0);
-
-	rb_sourcelist_update_expander_visibility (sourcelist);
 }
 
 static void
@@ -370,88 +452,133 @@ icon_notify_cb (RBSource *source,
 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (sourcelist->priv->treeview));
 }
 
+typedef struct {
+	const char *name;
+	gboolean    found;
+	GtkTreeIter iter;
+} FindGroup;
+
+/* adapted from gossip */
+static gboolean
+sourcelist_get_group_foreach (GtkTreeModel *model,
+			      GtkTreePath  *path,
+			      GtkTreeIter  *iter,
+			      FindGroup    *fg)
+{
+	char     *str;
+	gboolean  is_group;
+
+	/* Groups are only at the top level. */
+	if (gtk_tree_path_get_depth (path) != 1) {
+		return FALSE;
+	}
+
+	gtk_tree_model_get (model, iter,
+			    RB_SOURCELIST_MODEL_COLUMN_NAME, &str,
+			    RB_SOURCELIST_MODEL_COLUMN_IS_GROUP, &is_group,
+			    -1);
+	if (is_group && strcmp (str, fg->name) == 0) {
+		fg->found = TRUE;
+		fg->iter = *iter;
+	}
+
+	g_free (str);
+
+	return fg->found;
+}
+
+/* adapted from gossip */
+static void
+sourcelist_get_group (RBSourceList  *sourcelist,
+		      RBSourceGroup *group,
+		      GtkTreeIter   *iter_to_set,
+		      gboolean      *created)
+{
+	FindGroup fg;
+
+	memset (&fg, 0, sizeof (fg));
+
+	fg.name = group->display_name;
+
+	gtk_tree_model_foreach (sourcelist->priv->real_model,
+				(GtkTreeModelForeachFunc) sourcelist_get_group_foreach,
+				&fg);
+
+	if (! fg.found) {
+		if (created != NULL) {
+			*created = TRUE;
+		}
+
+		gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), iter_to_set, NULL);
+		gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model), iter_to_set,
+				    RB_SOURCELIST_MODEL_COLUMN_PIXBUF, NULL,
+				    RB_SOURCELIST_MODEL_COLUMN_NAME, group->display_name,
+				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, NULL,
+				    RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES, NULL,
+				    RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, TRUE,
+				    RB_SOURCELIST_MODEL_COLUMN_IS_GROUP, TRUE,
+				    RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY, group->category,
+				    -1);
+	} else {
+		if (created) {
+			*created = FALSE;
+		}
+
+		*iter_to_set = fg.iter;
+	}
+}
+
 void
 rb_sourcelist_append (RBSourceList *sourcelist,
-		      RBSource *source,
-		      RBSource *parent)
+		      RBSource     *source,
+		      RBSource     *parent)
 {
-	GtkTreeIter iter;
+	GtkTreeIter    iter;
 	PangoAttrList *attrs;
-	char *name;
-	GdkPixbuf *pixbuf;
-	gboolean visible;
+	char          *name;
+	GdkPixbuf     *pixbuf;
+	gboolean       visible;
+	RBSourceGroup *group;
+	RBSourceGroupCategory group_category;
+	GtkTreePath   *expand_path;
 
 	g_return_if_fail (RB_IS_SOURCELIST (sourcelist));
 	g_return_if_fail (RB_IS_SOURCE (source));
 
-	g_object_get (source, "name", &name, NULL);
-	g_object_get (source, "icon", &pixbuf, NULL);
-	g_object_get (source, "visibility", &visible, NULL);
+	expand_path = NULL;
+	group_category = RB_SOURCE_GROUP_CATEGORY_FIXED;
+
+	g_object_get (source,
+		      "name", &name,
+		      "icon", &pixbuf,
+		      "visibility", &visible,
+		      "source-group", &group,
+		      NULL);
 
 	if (parent) {
 		GtkTreeIter parent_iter;
 
-		rb_debug ("inserting source %p with parent", source);
+		rb_debug ("inserting source %p with parent %p", source, parent);
 		g_assert (rb_sourcelist_source_to_iter (sourcelist, parent, &parent_iter));
 		gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, &parent_iter);
-
-		if (visible) {
-			sourcelist->priv->child_source_count++;
-			rb_sourcelist_update_expander_visibility (sourcelist);
-		}
 	} else {
-		GtkTreePath *group_path;
-		GtkTreePath *prev_group_path = NULL;
-		RBSourceListGroup group;
-		GtkTreeIter group_iter;
-
-		/* get the marker rows before and after the group for this source */
-		g_object_get (G_OBJECT (source), "sourcelist-group", &group, NULL);
-		group_path = rb_sourcelist_model_get_group_path (RB_SOURCELIST_MODEL (sourcelist->priv->filter_model),
-								 group);
-		g_assert (group_path);
-
-		if (group > 0) {
-			/* there's no marker row before the fixed source group, but we
-			 * don't need one anyway.
-			 */
-			prev_group_path = rb_sourcelist_model_get_group_path (RB_SOURCELIST_MODEL (sourcelist->priv->filter_model),
-									      group-1);
-			g_assert (prev_group_path);
-		}
-
-		/* find the location to insert the source */
-		if (group == RB_SOURCELIST_GROUP_TRANSIENT) {
-			char *check_name = NULL;
+		GtkTreeIter    group_iter;
+		gboolean       created;
+		GtkTreePath   *path;
 
-			rb_debug ("inserting source %p in group %d in sorted order", source, group);
-			g_assert (prev_group_path);
+		g_return_if_fail (group);
 
-			gtk_tree_model_get_iter (sourcelist->priv->real_model, &group_iter, prev_group_path);
-			do {
-				g_free (check_name);
+		rb_debug ("inserting source %p to group %s", source, group->name);
 
-				if (!gtk_tree_model_iter_next (sourcelist->priv->real_model, &group_iter))
-					break;
+		sourcelist_get_group (sourcelist, group, &group_iter, &created);
 
-				gtk_tree_model_get (sourcelist->priv->real_model,
-						    &group_iter,
-						    RB_SOURCELIST_MODEL_COLUMN_NAME, &check_name,
-						    -1);
+		group_category = group->category;
 
-			} while (check_name && (strlen (check_name)) > 0 && (g_utf8_collate (name, check_name) > 0));
+		path = gtk_tree_model_get_path (sourcelist->priv->real_model, &group_iter);
+		expand_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (sourcelist->priv->filter_model), path);
+		gtk_tree_path_free (path);
 
-			g_free (check_name);
-
-		} else {
-			rb_debug ("inserting source %p in group %d", source, group);
-			gtk_tree_model_get_iter (sourcelist->priv->real_model, &group_iter, group_path);
-		}
-
-		gtk_tree_store_insert_before (GTK_TREE_STORE (sourcelist->priv->real_model),
-					      &iter, NULL, &group_iter);
-
-		gtk_tree_path_free (group_path);
+		gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, &group_iter);
 	}
 
 	attrs = pango_attr_list_new ();
@@ -462,6 +589,8 @@ rb_sourcelist_append (RBSourceList *sour
 			    RB_SOURCELIST_MODEL_COLUMN_SOURCE, source,
 			    RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES, attrs,
 			    RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, visible,
+			    RB_SOURCELIST_MODEL_COLUMN_IS_GROUP, FALSE,
+			    RB_SOURCELIST_MODEL_COLUMN_GROUP_CATEGORY, group_category,
 			    -1);
 
 	pango_attr_list_unref (attrs);
@@ -471,9 +600,14 @@ rb_sourcelist_append (RBSourceList *sour
 	}
 	g_free (name);
 
-	g_signal_connect_object (G_OBJECT (source), "notify::name", G_CALLBACK (name_notify_cb), sourcelist, 0);
-	g_signal_connect_object (G_OBJECT (source), "notify::visibility", G_CALLBACK (visibility_notify_cb), sourcelist, 0);
-	g_signal_connect_object (G_OBJECT (source), "notify::icon", G_CALLBACK (icon_notify_cb), sourcelist, 0);
+	g_signal_connect_object (source, "notify::name", G_CALLBACK (name_notify_cb), sourcelist, 0);
+	g_signal_connect_object (source, "notify::visibility", G_CALLBACK (visibility_notify_cb), sourcelist, 0);
+	g_signal_connect_object (source, "notify::icon", G_CALLBACK (icon_notify_cb), sourcelist, 0);
+
+	if (expand_path != NULL) {
+		gtk_tree_view_expand_row (GTK_TREE_VIEW (sourcelist->priv->treeview), expand_path, TRUE);
+		gtk_tree_path_free (expand_path);
+	}
 
 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (sourcelist->priv->treeview));
 }
@@ -566,7 +700,7 @@ rb_sourcelist_edit_source_name (RBSource
 
 	/* Make cell editable just for the moment.
 	   We'll turn it off once editing is done. */
-	g_object_set (G_OBJECT (sourcelist->priv->title_renderer), "editable", TRUE, NULL);
+	g_object_set (sourcelist->priv->title_renderer, "editable", TRUE, NULL);
 
 	gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (sourcelist->priv->treeview),
 					  path, sourcelist->priv->main_column,
@@ -607,22 +741,13 @@ rb_sourcelist_remove (RBSourceList *sour
 
 	g_assert (rb_sourcelist_source_to_iter (sourcelist, source, &iter));
 
-	if (gtk_tree_store_iter_depth (GTK_TREE_STORE (sourcelist->priv->real_model), &iter) > 0) {
-		gboolean visible;
-
-		g_object_get (source, "visibility", &visible, NULL);
-		if (visible)
-			sourcelist->priv->child_source_count--;
-		rb_sourcelist_update_expander_visibility (sourcelist);
-	}
-
 	if (source == sourcelist->priv->playing_source)
 		rb_sourcelist_set_playing_source (sourcelist, NULL);
 
 	gtk_tree_store_remove (GTK_TREE_STORE (sourcelist->priv->real_model), &iter);
-	g_signal_handlers_disconnect_by_func (G_OBJECT (source),
+	g_signal_handlers_disconnect_by_func (source,
 					      G_CALLBACK (name_notify_cb), sourcelist);
-        g_signal_handlers_disconnect_by_func (G_OBJECT (source),
+        g_signal_handlers_disconnect_by_func (source,
 					      G_CALLBACK (visibility_notify_cb),
                                              sourcelist);
 
@@ -659,7 +784,7 @@ rb_sourcelist_selection_changed_cb (GtkT
 
 	g_return_if_fail (RB_IS_SOURCE (target));
 	source = target;
-	g_signal_emit (G_OBJECT (sourcelist), rb_sourcelist_signals[SELECTED], 0, source);
+	g_signal_emit (sourcelist, rb_sourcelist_signals[SELECTED], 0, source);
 
 	g_object_unref (target);
 }
@@ -673,7 +798,7 @@ drop_received_cb (RBSourceListModel *mod
 {
 	rb_debug ("drop recieved");
 	/* Proxy the signal. */
-	g_signal_emit (G_OBJECT (sourcelist), rb_sourcelist_signals[DROP_RECEIVED], 0, target, data);
+	g_signal_emit (sourcelist, rb_sourcelist_signals[DROP_RECEIVED], 0, target, data);
 }
 
 static void
@@ -691,7 +816,7 @@ row_activated_cb (GtkTreeView *treeview,
 	gtk_tree_model_get (model, &iter, RB_SOURCELIST_MODEL_COLUMN_SOURCE, &target, -1);
 
 	if (target != NULL) {
-		g_signal_emit (G_OBJECT (sourcelist), rb_sourcelist_signals[SOURCE_ACTIVATED], 0, target);
+		g_signal_emit (sourcelist, rb_sourcelist_signals[SOURCE_ACTIVATED], 0, target);
 		g_object_unref (target);
 	}
 }
@@ -715,7 +840,7 @@ emit_show_popup (GtkTreeView *treeview,
 
 	g_return_val_if_fail (RB_IS_SOURCE (target), FALSE);
 
-	g_signal_emit (G_OBJECT (sourcelist), rb_sourcelist_signals[SHOW_POPUP], 0, target, &ret);
+	g_signal_emit (sourcelist, rb_sourcelist_signals[SHOW_POPUP], 0, target, &ret);
 
 	if (target != NULL) {
 		g_object_unref (target);
@@ -840,9 +965,6 @@ visibility_notify_cb (GObject *obj,
 				    -1);
 
 		if (old_visibility != new_visibility) {
-			sourcelist->priv->child_source_count += new_visibility ? 1 : -1;
-			rb_sourcelist_update_expander_visibility (sourcelist);
-
 			gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model), &iter,
 					    RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, new_visibility,
 					    -1);
@@ -867,7 +989,7 @@ rb_sourcelist_title_cell_data_func (GtkT
 			    RB_SOURCELIST_MODEL_COLUMN_PLAYING, &playing,
 			    -1);
 
-	g_object_set (G_OBJECT (renderer), "text", str,
+	g_object_set (renderer, "text", str,
 		      "weight", playing ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
 		      NULL);
 
@@ -896,31 +1018,13 @@ source_name_edited_cb (GtkCellRendererTe
 	if (source == NULL)
 		return;
 
-	g_object_set (G_OBJECT (source), "name", text, NULL);
+	g_object_set (source, "name", text, NULL);
 
 	gtk_tree_path_free (path);
 
-	g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
+	g_object_set (renderer, "editable", FALSE, NULL);
 
 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (sourcelist->priv->treeview));
 
 	g_object_unref (source);
-}
-
-static void
-rb_sourcelist_update_expander_visibility (RBSourceList *sourcelist)
-{
-	gboolean visible;
-	GtkTreeViewColumn *column;
-
-	g_assert (sourcelist->priv->child_source_count >= 0);
-
-	visible = (sourcelist->priv->child_source_count > 0);
-	if (visible)
-		column = sourcelist->priv->main_column;
-	else
-		column = sourcelist->priv->hidden_column;
-
-	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (sourcelist->priv->treeview),
-					   column);
 }
Index: sources/rb-static-playlist-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-static-playlist-source.c,v
retrieving revision 1.32
diff -u -p -r1.32 rb-static-playlist-source.c
--- sources/rb-static-playlist-source.c	14 Aug 2006 12:43:34 -0000	1.32
+++ sources/rb-static-playlist-source.c	6 Oct 2006 14:51:05 -0000
@@ -292,7 +292,7 @@ rb_static_playlist_source_new (RBShell *
 					"shell", shell,
 					"is-local", local,
 					"entry-type", entry_type,
-					"sourcelist-group", RB_SOURCELIST_GROUP_PERSISTANT,
+					"source-group", RB_SOURCE_GROUP_PLAYLISTS,
 					NULL));
 }
 
--- /dev/null	2006-09-27 22:18:42.000000000 +1000
+++ sources/rb-source-group.c	2006-10-07 00:21:09.000000000 +1000
@@ -0,0 +1,169 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2006  Jonathan Matthew <jonathan@kaolin.wh9.net>
+ *  Copyright (C) 2006  William Jon McCann <mccann@jhu.edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "rb-util.h"
+
+#include "rb-source-group.h"
+
+G_LOCK_DEFINE_STATIC (source_groups);
+
+static GHashTable *source_groups_map;
+
+static RBSourceGroup *library_group = RB_SOURCE_GROUP_INVALID;
+static RBSourceGroup *stores_group = RB_SOURCE_GROUP_INVALID;
+static RBSourceGroup *playlists_group = RB_SOURCE_GROUP_INVALID;
+static RBSourceGroup *devices_group = RB_SOURCE_GROUP_INVALID;
+static RBSourceGroup *shared_group = RB_SOURCE_GROUP_INVALID;
+
+static void
+register_core_source_groups (void)
+{
+	library_group = rb_source_group_register ("library");
+	library_group->display_name = g_strdup (_("Library"));
+	library_group->category = RB_SOURCE_GROUP_CATEGORY_FIXED;
+
+	stores_group = rb_source_group_register ("stores");
+	stores_group->display_name = g_strdup (_("STORES"));
+	stores_group->category = RB_SOURCE_GROUP_CATEGORY_FIXED;
+
+	playlists_group = rb_source_group_register ("playlists");
+	playlists_group->display_name = g_strdup (_("Playlists"));
+	playlists_group->category = RB_SOURCE_GROUP_CATEGORY_PERSISTANT;
+
+	devices_group = rb_source_group_register ("devices");
+	devices_group->display_name = g_strdup (_("Devices"));
+	devices_group->category = RB_SOURCE_GROUP_CATEGORY_REMOVABLE;
+
+	shared_group = rb_source_group_register ("shared");
+	shared_group->display_name = g_strdup(_("Shared"));
+	shared_group->category = RB_SOURCE_GROUP_CATEGORY_TRANSIENT;
+}
+
+void
+rb_source_group_init (void)
+{
+	G_LOCK (source_groups);
+	if (source_groups_map == NULL) {
+		source_groups_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	}
+	G_UNLOCK (source_groups);
+
+	register_core_source_groups ();
+}
+
+GType
+rb_source_group_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		type = g_boxed_type_register_static ("RBSourceGroup",
+						     (GBoxedCopyFunc)rb_copy_function,
+						     (GBoxedFreeFunc)rb_null_function);
+	}
+
+	return type;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+GType
+rb_source_group_category_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (RB_SOURCE_GROUP_CATEGORY_FIXED, "Fixed single instance source"),
+			ENUM_ENTRY (RB_SOURCE_GROUP_CATEGORY_PERSISTANT, "Persistant multiple-instance source"),
+			ENUM_ENTRY (RB_SOURCE_GROUP_CATEGORY_REMOVABLE, "Source representing a removable device"),
+			ENUM_ENTRY (RB_SOURCE_GROUP_CATEGORY_TRANSIENT, "Transient source (eg. network shares)"),
+			{ 0, 0, 0 }
+		};
+
+		etype = g_enum_register_static ("RBSourcelistGroupType", values);
+	}
+
+	return etype;
+}
+
+RBSourceGroup *
+rb_source_group_get_by_name (const char *name)
+{
+	gpointer t = NULL;
+
+	G_LOCK (source_groups);
+	if (source_groups_map) {
+		t = g_hash_table_lookup (source_groups_map, name);
+	}
+	G_UNLOCK (source_groups);
+
+	if (t)
+		return (RBSourceGroup *) t;
+
+	return RB_SOURCE_GROUP_INVALID;
+}
+
+RBSourceGroup *
+rb_source_group_register (const char *name)
+{
+	RBSourceGroup *group;
+
+	group = g_new0 (RBSourceGroup, 1);
+	group->category = RB_SOURCE_GROUP_CATEGORY_FIXED;
+	if (name) {
+		group->name = g_strdup (name);
+		G_LOCK (source_groups);
+		g_hash_table_insert (source_groups_map, g_strdup (group->name), group);
+		G_UNLOCK (source_groups);
+	}
+
+	return group;
+}
+
+RBSourceGroup *
+rb_source_group_library_get_type (void)
+{
+	return library_group;
+}
+
+RBSourceGroup *
+rb_source_group_playlists_get_type (void)
+{
+	return playlists_group;
+}
+
+RBSourceGroup *
+rb_source_group_devices_get_type (void)
+{
+	return devices_group;
+}
+
+RBSourceGroup *
+rb_source_group_shared_get_type (void)
+{
+	return shared_group;
+}
--- /dev/null	2006-09-27 22:18:42.000000000 +1000
+++ sources/rb-source-group.h	2006-10-07 00:20:32.000000000 +1000
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2006  Jonathan Matthew <jonathan@kaolin.wh9.net>
+ *  Copyright (C) 2006  William Jon McCann <mccann@jhu.edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#ifndef __RB_SOURCE_GROUP_H
+#define __RB_SOURCE_GROUP_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	RB_SOURCE_GROUP_CATEGORY_FIXED = 0,	/* library, iradio, podcast, queue */
+	RB_SOURCE_GROUP_CATEGORY_PERSISTANT,	/* playlists */
+	RB_SOURCE_GROUP_CATEGORY_REMOVABLE,	/* ipod, generic audio player, audio CD */
+	RB_SOURCE_GROUP_CATEGORY_TRANSIENT,	/* DAAP shares */
+	RB_SOURCE_GROUP_CATEGORY_LAST
+} RBSourceGroupCategory;
+
+GType rb_source_group_category_get_type (void);
+
+#define RB_TYPE_SOURCE_GROUP_CATEGORY (rb_source_group_category_get_type())
+
+
+struct RBSourceGroup
+{
+	char                 *name;
+	char                 *display_name;
+	RBSourceGroupCategory category;
+};
+
+typedef struct RBSourceGroup RBSourceGroup;
+
+GType          rb_source_group_get_type    (void);
+#define RB_TYPE_SOURCE_GROUP	(rb_source_group_get_type ())
+#define RB_SOURCE_GROUP(o)	(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_SOURCE_GROUP, RBSourceGroup))
+#define RB_IS_SOURCE_GROUP(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_SOURCE_GROUP))
+
+#define RB_SOURCE_GROUP_LIBRARY           (rb_source_group_library_get_type ())
+#define RB_SOURCE_GROUP_STORES            (rb_source_group_library_get_type ())
+#define RB_SOURCE_GROUP_PLAYLISTS         (rb_source_group_playlists_get_type ())
+#define RB_SOURCE_GROUP_DEVICES           (rb_source_group_devices_get_type ())
+#define RB_SOURCE_GROUP_SHARED            (rb_source_group_shared_get_type ())
+#define RB_SOURCE_GROUP_INVALID           (GINT_TO_POINTER (-1))
+
+void                  rb_source_group_init             (void);
+
+RBSourceGroup        *rb_source_group_get_by_name      (const char           *name);
+RBSourceGroup        *rb_source_group_register         (const char           *name);
+
+RBSourceGroup        *rb_source_group_library_get_type   (void);
+RBSourceGroup        *rb_source_group_stores_get_type   (void);
+RBSourceGroup        *rb_source_group_playlists_get_type (void);
+RBSourceGroup        *rb_source_group_devices_get_type   (void);
+RBSourceGroup        *rb_source_group_shared_get_type    (void);
+
+G_END_DECLS
+
+#endif /* __RB_SOURCE_GROUP_H */
