Index: lib/rb-util.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/lib/rb-util.c,v
retrieving revision 1.18
diff -u -u -r1.18 rb-util.c
--- lib/rb-util.c	30 Nov 2005 04:54:31 -0000	1.18
+++ lib/rb-util.c	7 Feb 2006 11:25:35 -0000
@@ -567,3 +567,103 @@
 	return str;
 }
 
+gboolean
+rb_string_list_equal (GList *a, GList *b)
+{
+	GList *sorted_a_keys;
+	GList *sorted_b_keys;
+	GList *a_ptr, *b_ptr;
+	gboolean ret = TRUE;
+
+	if (a == b)
+		return TRUE;
+
+	if (g_list_length (a) != g_list_length (b))
+		return FALSE;
+
+	for (sorted_a_keys = NULL; a; a = a->next) {
+		sorted_a_keys = g_list_prepend (sorted_a_keys,
+					       g_utf8_collate_key (a->data, -1));
+	}
+	for (sorted_b_keys = NULL; b; b = b->next) {
+		sorted_b_keys = g_list_prepend (sorted_b_keys,
+					       g_utf8_collate_key (b->data, -1));
+	}
+	sorted_a_keys = g_list_sort (sorted_a_keys, (GCompareFunc) strcmp);
+	sorted_b_keys = g_list_sort (sorted_b_keys, (GCompareFunc) strcmp);
+	
+	for (a_ptr = sorted_a_keys, b_ptr = sorted_b_keys;
+	     a_ptr && b_ptr; a_ptr = a_ptr->next, b_ptr = b_ptr->next) {
+		if (strcmp (a_ptr->data, b_ptr->data)) {
+			ret = FALSE;
+			break;
+		}
+	}
+	g_list_foreach (sorted_a_keys, (GFunc) g_free, NULL);
+	g_list_foreach (sorted_b_keys, (GFunc) g_free, NULL);
+	g_list_free (sorted_a_keys);
+	g_list_free (sorted_b_keys);
+	return ret;
+}
+
+static void
+list_copy_cb (const char *s, GList **list)
+{
+	*list = g_list_prepend (*list, g_strdup (s));
+}
+
+GList *
+rb_string_list_copy (GList *list)
+{
+	GList *copy = NULL;
+	
+	if (list == NULL)
+		return NULL;
+
+	g_list_foreach (list, (GFunc)list_copy_cb, &copy);
+	copy = g_list_reverse (copy);
+
+	return copy;
+}
+
+void
+rb_list_deep_free (GList *list)
+{
+	g_list_foreach (list, (GFunc)g_free, NULL);
+	g_list_free (list);
+}
+
+
+static void
+collate_keys_cb (gpointer key, gpointer value, GList **list)
+{
+	*list = g_list_prepend (*list, key);
+}
+
+static void
+collate_values_cb (gpointer key, gpointer value, GList **list)
+{
+	*list = g_list_prepend (*list, value);
+}
+
+GList*
+rb_collate_hash_table_keys (GHashTable *table)
+{
+	GList *list = NULL;
+
+	g_hash_table_foreach (table, (GHFunc)collate_keys_cb, &list);
+	list = g_list_reverse (list);
+
+	return list;
+}
+
+GList*
+rb_collate_hash_table_values (GHashTable *table)
+{
+	GList *list = NULL;
+
+	g_hash_table_foreach (table, (GHFunc)collate_values_cb, &list);
+	list = g_list_reverse (list);
+
+	return list;
+}
Index: lib/rb-util.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/lib/rb-util.h,v
retrieving revision 1.14
diff -u -u -r1.14 rb-util.h
--- lib/rb-util.h	30 Nov 2005 04:54:31 -0000	1.14
+++ lib/rb-util.h	7 Feb 2006 11:25:35 -0000
@@ -52,6 +52,13 @@
 gchar* rb_search_fold (const char *original);
 gchar** rb_string_split_words (const gchar *string);
 
+gboolean rb_string_list_equal (GList *a, GList *b);
+void rb_list_deep_free (GList *list);
+GList* rb_string_list_copy (GList *list);
+
+GList* rb_collate_hash_table_keys (GHashTable *table);
+GList* rb_collate_hash_table_values (GHashTable *table);
+
 G_END_DECLS
 
 #endif /* __RB_UTIL_H */
Index: rhythmdb/rhythmdb-property-model.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb-property-model.c,v
retrieving revision 1.31
diff -u -u -r1.31 rhythmdb-property-model.c
--- rhythmdb/rhythmdb-property-model.c	31 Jan 2006 15:57:35 -0000	1.31
+++ rhythmdb/rhythmdb-property-model.c	7 Feb 2006 11:25:35 -0000
@@ -98,6 +98,7 @@
 							  GList *paths);
 static gboolean rhythmdb_property_model_row_draggable (RbTreeDragSource *dragsource,
 						       GList *paths);
+
 enum {
 	TARGET_ALBUMS,
 	TARGET_GENRE,
@@ -139,6 +140,7 @@
 	RhythmDB *db;
 
 	RhythmDBQueryModel *query_model;
+	GHashTable *entries;
 
 	RhythmDBPropType propid;
 	RhythmDBPropType sort_propid;
@@ -168,7 +170,6 @@
 {
 	PROP_0,
 	PROP_RHYTHMDB,
-	PROP_QUERY,
 	PROP_PROP,
 	PROP_QUERY_MODEL,
 };
@@ -217,13 +218,6 @@
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	g_object_class_install_property (object_class,
-					 PROP_QUERY,
-					 g_param_spec_pointer ("query",
-							      "Query",
-							      "RhythmDBQuery",
-							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-	g_object_class_install_property (object_class,
 					 PROP_PROP,
 					 g_param_spec_int ("prop",
 							   "propid",
@@ -267,11 +261,38 @@
 	iface->drag_data_get = rhythmdb_property_model_drag_data_get;
 }
 
+static inline RhythmDBEntry *
+entry_from_tree_iter (GtkTreeModel *model, GtkTreeIter *iter)
+{
+	RhythmDBEntry *entry;
+	gtk_tree_model_get (model, iter, 0, &entry, -1);
+	return entry;
+}
+
+static gboolean
+_remove_entry_cb (RhythmDBEntry *entry,
+		  gpointer unused,
+		  RhythmDBPropertyModel *model)
+{
+	rhythmdb_property_model_delete (model, entry);
+	return TRUE;
+}
+
+static gboolean
+_add_entry_cb (GtkTreeModel *model,
+	       GtkTreePath *path,
+	       GtkTreeIter *iter,
+	       RhythmDBPropertyModel *propmodel)
+{
+	rhythmdb_property_model_row_inserted_cb (model, path, iter, propmodel);
+	return FALSE;
+}
+
 static void
 rhythmdb_property_model_set_property (GObject *object,
-				   guint prop_id,
-				   const GValue *value,
-				   GParamSpec *pspec)
+				      guint prop_id,
+				      const GValue *value,
+				      GParamSpec *pspec)
 {
 	RhythmDBPropertyModel *model = RHYTHMDB_PROPERTY_MODEL (object);
 
@@ -281,11 +302,6 @@
 		model->priv->db = g_value_get_object (value);
 		break;
 	}
-	case PROP_QUERY:
-		if (model->priv->query)
-			rhythmdb_query_free (model->priv->query);
-		model->priv->query = rhythmdb_query_copy (g_value_get_pointer (value));
-		break;
 	case PROP_PROP:
 		model->priv->propid = g_value_get_int (value);
 		switch (model->priv->propid) {
@@ -322,12 +338,15 @@
 			g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->query_model),
 							      G_CALLBACK (rhythmdb_property_model_prop_changed_cb),
 							      model);
+			g_hash_table_foreach_remove (model->priv->entries, (GHRFunc)_remove_entry_cb, model);
 			g_object_unref (G_OBJECT (model->priv->query_model));
 		}
 		
 		model->priv->query_model = g_value_get_object (value);
+		g_assert (rhythmdb_property_model_iter_n_children (GTK_TREE_MODEL (model), NULL) == 1);
 
 		if (model->priv->query_model) {
+			g_object_ref (G_OBJECT (model->priv->query_model));
 			g_signal_connect_object (G_OBJECT (model->priv->query_model),
 						 "row_inserted",
 						 G_CALLBACK (rhythmdb_property_model_row_inserted_cb),
@@ -343,7 +362,9 @@
 						 G_CALLBACK (rhythmdb_property_model_prop_changed_cb),
 						 model,
 						 0);
-			g_object_ref (G_OBJECT (model->priv->query_model));
+			gtk_tree_model_foreach (GTK_TREE_MODEL (model->priv->query_model),
+						(GtkTreeModelForeachFunc)_add_entry_cb,
+						model);
 		}
 		break;
 	}
@@ -365,9 +386,6 @@
 	case PROP_RHYTHMDB:
 		g_value_set_object (value, model->priv->db);
 		break;
-	case PROP_QUERY:
-		g_value_set_pointer (value, model->priv->query);
-		break;
 	case PROP_PROP:
 		g_value_set_int (value, model->priv->propid);
 		break;
@@ -389,6 +407,7 @@
 
 	model->priv->properties = g_sequence_new (NULL);
 	model->priv->reverse_map = g_hash_table_new (g_str_hash, g_str_equal);
+	model->priv->entries = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	model->priv->all = g_new0 (RhythmDBPropertyModelEntry, 1);
 	model->priv->all->string = rb_refstring_new (_("All"));
@@ -423,6 +442,7 @@
 
 	g_hash_table_destroy (model->priv->reverse_map);
 	g_sequence_free (model->priv->properties);
+	g_hash_table_destroy (model->priv->entries);
 
 	if (model->priv->query_model)
 		g_object_unref (G_OBJECT (model->priv->query_model));
@@ -436,14 +456,6 @@
 	return g_object_new (RHYTHMDB_TYPE_PROPERTY_MODEL, "db", db, "prop", propid, NULL);
 }
 
-static inline RhythmDBEntry *
-entry_from_tree_iter (GtkTreeModel *model, GtkTreeIter *iter)
-{
-	RhythmDBEntry *entry;
-	gtk_tree_model_get (model, iter, 0, &entry, -1);
-	return entry;
-}
-
 static void
 rhythmdb_property_model_row_inserted_cb (GtkTreeModel *model,
 					 GtkTreePath *path,
@@ -452,10 +464,13 @@
 {
 	RhythmDBEntry *entry = entry_from_tree_iter (model, iter);
 
-	if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN) == FALSE) {
-		rhythmdb_property_model_insert (propmodel, entry);
-		rhythmdb_property_model_sync (propmodel);
-	}
+	if (g_hash_table_lookup (propmodel->priv->entries, entry))
+		return;
+
+	g_hash_table_insert (propmodel->priv->entries, entry, NULL);
+
+	rhythmdb_property_model_insert (propmodel, entry);
+	rhythmdb_property_model_sync (propmodel);
 }
 
 static void
@@ -465,30 +480,34 @@
 					 RhythmDBPropertyModel *propmodel)
 {
 	if (prop == RHYTHMDB_PROP_HIDDEN) {
-		gboolean old_hidden = g_value_get_boolean (old);
-		gboolean new_hidden = g_value_get_boolean (new);
+		gboolean old_val = g_value_get_boolean (old);
+		gboolean new_val = g_value_get_boolean (new);
 
-		if (new_hidden == old_hidden)
-			return;
-
-		rb_debug ("recieving hidden-change notification for %s", entry->location);
-
-		if (new_hidden)
-			rhythmdb_property_model_delete (propmodel, entry);
-		else
-			rhythmdb_property_model_insert (propmodel, entry);
+		if (old_val != new_val) {
+			if (new_val) {
+				if (g_hash_table_lookup (propmodel->priv->entries, entry) != NULL)
+					return;
+
+				rhythmdb_property_model_insert (propmodel, entry);
+			} else {
+				if (g_hash_table_lookup (propmodel->priv->entries, entry) == NULL)
+					return;
 
-		rhythmdb_property_model_sync (propmodel);
-	} else {	
+				rhythmdb_property_model_delete (propmodel, entry);
+			}
+		}
+	} else {
 		if (prop != propmodel->priv->propid)
 			return;
-		if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
+
+		if (g_hash_table_lookup (propmodel->priv->entries, entry))
 			return;
 
 		rhythmdb_property_model_delete_prop (propmodel, g_value_get_string (old));
 		rhythmdb_property_model_insert (propmodel, entry);
-		rhythmdb_property_model_sync (propmodel);
 	}
+
+	rhythmdb_property_model_sync (propmodel);
 }
 
 static void
@@ -496,10 +515,13 @@
 					  RhythmDBEntry *entry,
 					  RhythmDBPropertyModel *propmodel)
 {
-	if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN) == FALSE) {
-		rhythmdb_property_model_delete (propmodel, entry);
-		rhythmdb_property_model_sync (propmodel);
-	}
+	if (g_hash_table_lookup (propmodel->priv->entries, entry) == NULL)
+		return;
+
+	g_hash_table_remove (propmodel->priv->entries, entry);
+
+	rhythmdb_property_model_delete (propmodel, entry);
+	rhythmdb_property_model_sync (propmodel);
 }
 
 static gint
@@ -552,7 +574,7 @@
 	gtk_tree_path_free (path);
 }
 
-void
+static void
 rhythmdb_property_model_delete (RhythmDBPropertyModel *model,
 				RhythmDBEntry *entry)
 {
Index: rhythmdb/rhythmdb-query-model.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb-query-model.c,v
retrieving revision 1.99
diff -u -u -r1.99 rhythmdb-query-model.c
--- rhythmdb/rhythmdb-query-model.c	5 Feb 2006 09:16:50 -0000	1.99
+++ rhythmdb/rhythmdb-query-model.c	7 Feb 2006 11:25:38 -0000
@@ -451,11 +451,13 @@
 			g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 							      G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
 							      model);
+			g_object_unref (model->priv->base_model);
 		}
 
 		model->priv->base_model = g_value_get_object (value);
 
 		if (model->priv->base_model) {
+			g_object_ref (model->priv->base_model);
 			g_assert (model->priv->base_model->priv->db == model->priv->db);
 
 			g_signal_connect_object (G_OBJECT (model->priv->base_model),
@@ -632,7 +634,6 @@
 		rhythmdb_query_free (model->priv->original_query);
 
 	if (model->priv->base_model) {
-		g_object_unref (G_OBJECT (model->priv->base_model));
 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 						      G_CALLBACK (rhythmdb_query_model_base_row_inserted),
 						      model);
@@ -651,6 +652,7 @@
 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 						      G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
 						      model);
+		g_object_unref (G_OBJECT (model->priv->base_model));
 	}
 
 
@@ -1767,6 +1769,43 @@
 					       rhythmdb_query_model_get_size (model));
 }
 
+static void
+apply_updated_entry_sequence (RhythmDBQueryModel *model, GSequence *new_entries)
+{
+	int *reorder_map;
+	int length, i;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	GSequencePtr ptr;
+
+	length = g_sequence_get_length (new_entries);
+	/* generate resort map and rebuild reverse map */
+	reorder_map = malloc (length * sizeof(gint));
+
+	ptr = g_sequence_get_begin_ptr (new_entries);
+	for (i = 0; i < length; i++) {
+		gpointer entry = g_sequence_ptr_get_data (ptr);
+		GSequencePtr old_ptr;
+
+		old_ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
+		reorder_map[i] = g_sequence_ptr_get_position (ptr);
+		g_hash_table_replace (model->priv->reverse_map, entry, ptr);
+
+		ptr = g_sequence_ptr_next (ptr);
+	}
+
+	/* emit the re-order and clean up */
+	gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
+	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
+				       path, &iter,
+				       reorder_map);
+
+	gtk_tree_path_free (path);
+	free (reorder_map);
+	g_sequence_free (model->priv->entries);
+	model->priv->entries = new_entries;
+}
 
 void
 rhythmdb_query_model_set_sort_order (RhythmDBQueryModel *model,
@@ -1776,10 +1815,7 @@
 {
 	GSequence *new_entries;
 	GSequencePtr ptr;
-	GtkTreePath *path;
-	GtkTreeIter iter;
 	int length, i;
-	int *reorder_map;
 
 	g_return_if_fail (((model->priv->max_count == 0) &&
 			  (model->priv->max_time == 0) &&
@@ -1809,32 +1845,7 @@
 			ptr = g_sequence_ptr_next (ptr);
 		}
 
-		/* generate resort map and rebuild reverse map */
-		reorder_map = malloc (length * sizeof(gint));
-
-		ptr = g_sequence_get_begin_ptr (new_entries);
-		for (i = 0; i < length; i++) {
-			gpointer entry = g_sequence_ptr_get_data (ptr);
-			GSequencePtr old_ptr;
-		       
-			old_ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
-			reorder_map[i] = g_sequence_ptr_get_position (ptr);
-			g_hash_table_replace (model->priv->reverse_map, entry, ptr);
-
-			ptr = g_sequence_ptr_next (ptr);
-		}
-
-		/* emit the re-order and clean up */
-		gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
-		path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
-		gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
-					       path, &iter,
-					       reorder_map);
-
-		gtk_tree_path_free (path);
-		free (reorder_map);
-		g_sequence_free (model->priv->entries);
-		model->priv->entries = new_entries;
+		apply_updated_entry_sequence (model, new_entries);
 	}
 }
 
@@ -1856,7 +1867,7 @@
 	return g_sequence_ptr_get_position (ptr);
 }
 
-static int
+/*static int
 rhythmdb_query_model_base_index_to_child_index (RhythmDBQueryModel *model, int index)
 {
 	GSequencePtr ptr;
@@ -1877,9 +1888,8 @@
 		return -1;
 
 	pos = g_sequence_ptr_get_position (ptr);
-	g_assert (index == rhythmdb_query_model_child_index_to_base_index (model, pos));
 	return pos;
-}
+}*/
 
 static int
 rhythmdb_query_model_get_entry_index (RhythmDBQueryModel *model, RhythmDBEntry *entry)
@@ -1977,30 +1987,28 @@
 					  gint *order_map,
 					  RhythmDBQueryModel *model)
 {
-	gint new_length;
-	gint *new_map;
-	gint old_length;
-	gint i;
-	GtkTreePath *path;
-	GtkTreeIter iter;
+	RhythmDBQueryModel *base_query_model = RHYTHMDB_QUERY_MODEL (base_model);
+	GSequence *new_entries;
+	GSequencePtr ptr;
+
+	/* ignore, if this model sorts */
+	if (model->priv->sort_func)
+		return;
+
+	new_entries = g_sequence_new (NULL);
+
+	/* iterate over the entries in the base, and recreate the sequence */
+	ptr = g_sequence_get_begin_ptr (base_query_model->priv->entries);
+	while (!g_sequence_ptr_is_end (ptr)) {
+		gpointer entry = g_sequence_ptr_get_data (ptr);
 
-	old_length = g_sequence_get_length (RHYTHMDB_QUERY_MODEL (base_model)->priv->entries);
-	new_length = g_sequence_get_length (model->priv->entries);
-	new_map = malloc (new_length * sizeof(gint));
-
-	/* convert the reorder map from the base to child models */
-	for (i = 0; i < new_length; i++) {
-		gint old_index = rhythmdb_query_model_child_index_to_base_index (model, i);
-		gint new_index = rhythmdb_query_model_base_index_to_child_index (model, order_map[old_index]);
+		if (g_hash_table_lookup (model->priv->reverse_map, entry))
+			g_sequence_append (new_entries, entry);
 
-		new_map[i] = new_index;
+		ptr = g_sequence_ptr_next (ptr);
 	}
 
-	gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
-	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
-	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), path, NULL, new_map);
-	gtk_tree_path_free (path);
-	free (new_map);
+	apply_updated_entry_sequence (model, new_entries);
 }
 
 void
Index: rhythmdb/rhythmdb-tree.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb-tree.c,v
retrieving revision 1.91
diff -u -u -r1.91 rhythmdb-tree.c
--- rhythmdb/rhythmdb-tree.c	7 Feb 2006 08:07:24 -0000	1.91
+++ rhythmdb/rhythmdb-tree.c	7 Feb 2006 11:25:40 -0000
@@ -1819,8 +1819,10 @@
 {
 	GList *conjunctions, *tem;
 
+	if (query == NULL)
+		return;
+	
 	conjunctions = split_query_by_disjunctions (db, query);
-
 	rb_debug ("doing recursive query, %d conjunctions", g_list_length (conjunctions));
 
 	if (conjunctions == NULL)
Index: rhythmdb/rhythmdb.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb.c,v
retrieving revision 1.168
diff -u -u -r1.168 rhythmdb.c
--- rhythmdb/rhythmdb.c	5 Feb 2006 09:42:15 -0000	1.168
+++ rhythmdb/rhythmdb.c	7 Feb 2006 11:25:42 -0000
@@ -2784,15 +2784,28 @@
 GPtrArray *
 rhythmdb_query_copy (GPtrArray *array)
 {
-	guint i;
 	GPtrArray *ret;
 
 	if (!array)
 		return NULL;
 	
-	ret = g_ptr_array_sized_new (array->len);	
-	for (i = 0; i < array->len; i++) {
-		RhythmDBQueryData *data = g_ptr_array_index (array, i);
+	ret = g_ptr_array_sized_new (array->len);
+	rhythmdb_query_concatenate (ret, array);
+
+	return ret;
+}
+
+void
+rhythmdb_query_concatenate (GPtrArray *query1, GPtrArray *query2)
+{
+	guint i;
+
+	g_assert (query2);
+	if (!query2)
+		return;
+	
+	for (i = 0; i < query2->len; i++) {
+		RhythmDBQueryData *data = g_ptr_array_index (query2, i);
 		RhythmDBQueryData *new_data = g_new0 (RhythmDBQueryData, 1);
 		new_data->type = data->type;
 		new_data->propid = data->propid;
@@ -2803,12 +2816,11 @@
 		}
 		if (data->subquery)
 			new_data->subquery = rhythmdb_query_copy (data->subquery);
-		g_ptr_array_add (ret, new_data);
+		g_ptr_array_add (query1, new_data);
 	}
-
-	return ret;
 }
 
+
 static GPtrArray *
 rhythmdb_query_parse_valist (RhythmDB *db, va_list args)
 {
@@ -3849,6 +3861,9 @@
 {
 	int i;	
 
+	if (query == NULL)
+		return;
+
 	for (i = 0; i < query->len; i++) {
 		RhythmDBQueryData *data = g_ptr_array_index (query, i);
 		gboolean restart_criteria = FALSE;
@@ -3945,6 +3960,48 @@
 			i--;
 	}
 }
+
+void
+rhythmdb_query_append_prop_multiple (RhythmDB *db, GPtrArray *query, guint propid, GList *items)
+{
+	GPtrArray *subquery;
+
+	if (items == NULL)
+		return;
+
+	if (items->next == NULL) {
+		rhythmdb_query_append (db,
+				       query,
+				       RHYTHMDB_QUERY_PROP_EQUALS,
+				       propid,
+				       items->data,
+				       RHYTHMDB_QUERY_END);
+		return;
+	}
+
+	subquery = g_ptr_array_new ();
+
+	rhythmdb_query_append (db,
+			       subquery,
+			       RHYTHMDB_QUERY_PROP_EQUALS,
+			       propid,
+			       items->data,
+			       RHYTHMDB_QUERY_END);
+	items = items->next;
+	while (items) {
+		rhythmdb_query_append (db,
+				       subquery,
+				       RHYTHMDB_QUERY_DISJUNCTION,
+				       RHYTHMDB_QUERY_PROP_EQUALS,
+				       propid,
+				       items->data,
+				       RHYTHMDB_QUERY_END);
+		items = items->next;
+	}
+	rhythmdb_query_append (db, query, RHYTHMDB_QUERY_SUBQUERY, subquery,
+			       RHYTHMDB_QUERY_END);
+}
+
 
 static gboolean
 rhythmdb_idle_save (RhythmDB *db)
Index: rhythmdb/rhythmdb.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb.h,v
retrieving revision 1.66
diff -u -u -r1.66 rhythmdb.h
--- rhythmdb/rhythmdb.h	5 Feb 2006 09:16:50 -0000	1.66
+++ rhythmdb/rhythmdb.h	7 Feb 2006 11:25:42 -0000
@@ -536,6 +536,8 @@
 
 GPtrArray *	rhythmdb_query_parse			(RhythmDB *db, ...);
 void		rhythmdb_query_append			(RhythmDB *db, GPtrArray *query, ...);
+void		rhythmdb_query_append_prop_multiple	(RhythmDB *db, GPtrArray *query, RhythmDBPropType propid, GList *items);
+void		rhythmdb_query_concatenate		(GPtrArray *query1, GPtrArray *query2);
 void		rhythmdb_query_free			(GPtrArray *query);
 GPtrArray *	rhythmdb_query_copy			(GPtrArray *array);
 void		rhythmdb_query_preprocess		(RhythmDB *db, GPtrArray *query);
Index: shell/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/Makefile.am,v
retrieving revision 1.67
diff -u -u -r1.67 Makefile.am
--- shell/Makefile.am	6 Feb 2006 10:38:44 -0000	1.67
+++ shell/Makefile.am	7 Feb 2006 11:25:42 -0000
@@ -80,13 +80,13 @@
 	librbshell.la					\
 	$(top_builddir)/sources/libsources.la	        \
 	$(top_builddir)/sources/libsourcesimpl.la	\
-	$(top_builddir)/rhythmdb/librhythmdb.la         \
 	$(top_builddir)/iradio/librbiradio.la	        \
 	$(top_builddir)/podcast/librbpodcast.la	        \
 	$(top_builddir)/remote/librbremote.la	        \
 	$(top_builddir)/player/librbplayer.la		\
 	$(top_builddir)/metadata/librbmetadata.la	\
 	$(top_builddir)/widgets/librbwidgets.la         \
+	$(top_builddir)/rhythmdb/librhythmdb.la         \
 	$(top_builddir)/lib/librb.la	                \
 	$(GDA_LIBS)					\
 	$(TOTEM_PLPARSER_LIBS)				\
Index: shell/rb-shell-player.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-shell-player.c,v
retrieving revision 1.209
diff -u -u -r1.209 rb-shell-player.c
--- shell/rb-shell-player.c	6 Feb 2006 10:54:45 -0000	1.209
+++ shell/rb-shell-player.c	7 Feb 2006 11:25:46 -0000
@@ -633,16 +633,16 @@
 	case PROP_SOURCE:
 		if (player->priv->selected_source != NULL) {
 			RBEntryView *songs = rb_source_get_entry_view (player->priv->selected_source);
-			GList *extra_views = rb_source_get_extra_views (player->priv->selected_source);
+			GList *property_views = rb_source_get_property_views (player->priv->selected_source);
 
 			g_signal_handlers_disconnect_by_func (G_OBJECT (songs),
 							      G_CALLBACK (rb_shell_player_entry_activated_cb),
 							      player);
-			for (; extra_views; extra_views = extra_views->next)
-				g_signal_handlers_disconnect_by_func (G_OBJECT (extra_views->data),
+			for (; property_views; property_views = property_views->next)
+				g_signal_handlers_disconnect_by_func (G_OBJECT (property_views->data),
 								      G_CALLBACK (rb_shell_player_property_row_activated_cb),
 								      player);
-			g_list_free (extra_views);
+			g_list_free (property_views);
 			
 		}
 
@@ -655,19 +655,19 @@
 
 		if (player->priv->selected_source != NULL) {
 			RBEntryView *songs = rb_source_get_entry_view (player->priv->selected_source);
-			GList *extra_views = rb_source_get_extra_views (player->priv->selected_source);
+			GList *property_views = rb_source_get_property_views (player->priv->selected_source);
 
 			g_signal_connect_object (G_OBJECT (songs),
 						 "entry-activated",
 						 G_CALLBACK (rb_shell_player_entry_activated_cb),
 						 player, 0);
-			for (; extra_views; extra_views = extra_views->next)
-				g_signal_connect_object (G_OBJECT (extra_views->data),
+			for (; property_views; property_views = property_views->next)
+				g_signal_connect_object (G_OBJECT (property_views->data),
 							 "property-activated",
 							 G_CALLBACK (rb_shell_player_property_row_activated_cb),
 							 player, 0);
 
-			g_list_free (extra_views);
+			g_list_free (property_views);
 		}
 
 		/* If we're not playing, change the play order's view of the current source;
Index: shell/rb-source-header.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-source-header.c,v
retrieving revision 1.38
diff -u -u -r1.38 rb-source-header.c
--- shell/rb-source-header.c	12 Dec 2005 04:16:37 -0000	1.38
+++ shell/rb-source-header.c	7 Feb 2006 11:25:46 -0000
@@ -428,6 +428,7 @@
 				   header->priv->disclosed);
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (viewbrowser_action),
 				      header->priv->disclosed);
+	rb_source_browser_toggled (header->priv->selected_source, header->priv->disclosed);
 }
 
 static void
Index: sources/rb-auto-playlist-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-auto-playlist-source.c,v
retrieving revision 1.2
diff -u -u -r1.2 rb-auto-playlist-source.c
--- sources/rb-auto-playlist-source.c	11 Dec 2005 11:58:14 -0000	1.2
+++ sources/rb-auto-playlist-source.c	7 Feb 2006 11:25:47 -0000
@@ -27,19 +27,25 @@
 #include <string.h>
 
 #include "rb-auto-playlist-source.h"
+#include "rb-library-browser.h"
 #include "rb-util.h"
 #include "rb-debug.h"
+#include "eel-gconf-extensions.h"
 #include "rb-stock-icons.h"
 #include "rb-playlist-xml.h"
 
+
 static GObject *rb_auto_playlist_source_constructor (GType type, guint n_construct_properties,
 						      GObjectConstructParam *construct_properties);
+static void rb_auto_playlist_source_finalize (GObject *object);
+
 /* source methods */
 static GdkPixbuf *impl_get_pixbuf (RBSource *source);
 static gboolean impl_show_popup (RBSource *source);
 static gboolean impl_receive_drag (RBSource *asource, GtkSelectionData *data);
 static void impl_search (RBSource *asource, const char *search_text);
 static void impl_reset_filters (RBSource *asource);
+static void impl_browser_toggled (RBSource *source, gboolean enabled);
 
 /* playlist methods */
 static void impl_save_contents_to_xml (RBPlaylistSource *source,
@@ -49,18 +55,31 @@
 								 RBAutoPlaylistSource *source); 
 static void rb_auto_playlist_source_do_query (RBAutoPlaylistSource *source);
 
+/* browser stuff */
+static GList *impl_get_property_views (RBSource *source);
+void rb_auto_playlist_source_browser_views_activated_cb (GtkWidget *widget,
+							 RBAutoPlaylistSource *source);
+static void rb_auto_playlist_source_browser_changed_cb (RBLibraryBrowser *entry,
+							RBAutoPlaylistSource *source);
+
+
 #define AUTO_PLAYLIST_SOURCE_POPUP_PATH "/AutoPlaylistSourcePopup"
 
 typedef struct _RBAutoPlaylistSourcePrivate RBAutoPlaylistSourcePrivate;
 
 struct _RBAutoPlaylistSourcePrivate
 {
+	RhythmDBQueryModel *cached_all_query;
 	GPtrArray *query;
 	gboolean query_resetting;
-	char *search_text;
 	guint limit_count;
 	guint limit_mb;
 	guint limit_time;
+
+	GtkWidget *paned;
+	RBLibraryBrowser *browser;
+
+	char *search_text;
 };
 
 G_DEFINE_TYPE (RBAutoPlaylistSource, rb_auto_playlist_source, RB_TYPE_PLAYLIST_SOURCE)
@@ -76,15 +95,19 @@
 	RBPlaylistSourceClass *playlist_class = RB_PLAYLIST_SOURCE_CLASS (klass);
 
 	object_class->constructor = rb_auto_playlist_source_constructor;
+	object_class->finalize = rb_auto_playlist_source_finalize;
 
 	source_class->impl_get_pixbuf = impl_get_pixbuf;
 	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_receive_drag = impl_receive_drag;
 	source_class->impl_show_popup = impl_show_popup;
+	source_class->impl_can_browse = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_browser_toggled = impl_browser_toggled;
 	source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_search = impl_search;
 	source_class->impl_reset_filters = impl_reset_filters;
+	source_class->impl_get_property_views = impl_get_property_views;
 
 	playlist_class->impl_save_contents_to_xml = impl_save_contents_to_xml;
 	
@@ -100,6 +123,18 @@
 static void
 rb_auto_playlist_source_init (RBAutoPlaylistSource *source)
 {
+
+}
+
+static void
+rb_auto_playlist_source_finalize (GObject *object)
+{
+	RBAutoPlaylistSourcePrivate *priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (object);
+
+	if (priv->cached_all_query)
+		g_object_unref (G_OBJECT (priv->cached_all_query));
+
+	G_OBJECT_CLASS (rb_auto_playlist_source_parent_class)->finalize (object);
 }
 
 static GObject *
@@ -109,14 +144,34 @@
 	RBEntryView *songs;
 	RBAutoPlaylistSource *source;
 	GObjectClass *parent_class = G_OBJECT_CLASS (rb_auto_playlist_source_parent_class);
+	RBAutoPlaylistSourcePrivate *priv;
 
 	source = RB_AUTO_PLAYLIST_SOURCE (
 			parent_class->constructor (type, n_construct_properties, construct_properties));
+	priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (source);
+
+	priv->paned = gtk_vpaned_new ();
+
+	priv->browser = rb_library_browser_new (rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source)));
+	gtk_paned_pack1 (GTK_PANED (priv->paned), GTK_WIDGET (priv->browser), TRUE, FALSE);
+	g_signal_connect_object (G_OBJECT (priv->browser), "changed",
+				 G_CALLBACK (rb_auto_playlist_source_browser_changed_cb),
+				 source, 0);
+
 
 	songs = rb_source_get_entry_view (RB_SOURCE (source));
 	g_signal_connect_object (G_OBJECT (songs), "sort-order-changed",
-				 G_CALLBACK (rb_auto_playlist_source_songs_sort_order_changed_cb), source, 0);
+				 G_CALLBACK (rb_auto_playlist_source_songs_sort_order_changed_cb),
+				 source, 0);
+
+	/* reparent the entry view */
+	g_object_ref (G_OBJECT (songs));
+	gtk_container_remove (GTK_CONTAINER (source), GTK_WIDGET (songs));
+	gtk_paned_pack2 (GTK_PANED (priv->paned), GTK_WIDGET (songs), TRUE, FALSE);
+	gtk_container_add (GTK_CONTAINER (source), priv->paned);
 	
+	gtk_widget_show_all (GTK_WIDGET (source));
+
 	return G_OBJECT (source);
 }
 
@@ -214,6 +269,9 @@
 {
 	RBAutoPlaylistSourcePrivate *priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (source);
 	gboolean changed = FALSE;
+	
+	if (rb_library_browser_reset (priv->browser))
+		changed = TRUE;
 
 	if (priv->search_text != NULL) {
 		changed = TRUE;
@@ -250,6 +308,16 @@
 	rb_source_notify_filter_changed (source);
 }
 
+static GList *
+impl_get_property_views (RBSource *source)
+{
+	RBAutoPlaylistSourcePrivate *priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (source);
+	GList *ret;
+
+	ret =  rb_library_browser_get_property_views (priv->browser);
+	return ret;
+}
+
 static RhythmDBPropType
 rb_auto_playlist_source_drag_atom_to_prop (GdkAtom smasher)
 {
@@ -265,6 +333,17 @@
 	}
 }
 
+static void
+impl_browser_toggled (RBSource *source, gboolean enabled)
+{
+	RBAutoPlaylistSourcePrivate *priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (source);
+
+	if (enabled)
+		gtk_widget_show (GTK_WIDGET (priv->browser));
+	else
+		gtk_widget_hide (GTK_WIDGET (priv->browser));
+}
+
 static gboolean
 impl_receive_drag (RBSource *asource, GtkSelectionData *data)
 {
@@ -362,37 +441,74 @@
 	rhythmdb_query_serialize (rb_playlist_source_get_db (psource), query, node);
 }
 
-static void
-rb_auto_playlist_source_do_query (RBAutoPlaylistSource *source)
+static GPtrArray *
+construct_query_from_selection (RBAutoPlaylistSource *source)
 {
 	RBAutoPlaylistSourcePrivate *priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (source);
 	RhythmDB *db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source));
-	RhythmDBQueryModel *model;
-
-	model = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
-			      "db", db,
-			      "max-count", priv->limit_count,
-			      "max-size", priv->limit_mb, 
-			      "max-time", priv->limit_time, 
-			      NULL);
+	GPtrArray *query = rhythmdb_query_copy (priv->query);
+	GPtrArray *browser_query;
 
 	if (priv->search_text) {
-		GPtrArray *query = rhythmdb_query_copy (priv->query);
-
+		GPtrArray *subquery = rhythmdb_query_parse (db,
+							    RHYTHMDB_QUERY_PROP_LIKE,
+							    RHYTHMDB_PROP_SEARCH_MATCH,
+							    priv->search_text,
+							    RHYTHMDB_QUERY_END);
 		rhythmdb_query_append (db,
 				       query,
-				       RHYTHMDB_QUERY_PROP_LIKE,
-				       RHYTHMDB_PROP_SEARCH_MATCH,
-				       priv->search_text,
+				       RHYTHMDB_QUERY_SUBQUERY,
+				       subquery,
 				       RHYTHMDB_QUERY_END);
-		rhythmdb_do_full_query_async_parsed (db, GTK_TREE_MODEL (model), query);
-		rhythmdb_query_free (query);
-	} else {
-		rhythmdb_do_full_query_async_parsed (db, GTK_TREE_MODEL (model), priv->query);
+		/* select where type="song" and
+		 *  (genre like "foo" or artist like "foo" or album like "foo")
+		 */
 	}
-	rb_playlist_source_set_query_model (RB_PLAYLIST_SOURCE (source), model);
+
+	browser_query = rb_library_browser_construct_query (priv->browser);
+	rhythmdb_query_concatenate (query, browser_query);
+	rhythmdb_query_free (browser_query);
+
+	return query;
 }
 
+static void
+rb_auto_playlist_source_do_query (RBAutoPlaylistSource *source)
+{
+	RBAutoPlaylistSourcePrivate *priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (source);
+	RhythmDB *db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source));
+	RhythmDBQueryModel *query_model;
+	gboolean is_all_query;
+
+	g_assert (priv->cached_all_query);
+	
+	is_all_query = (!rb_library_browser_has_selection (priv->browser) &&
+			(priv->search_text == NULL));
+
+	if (is_all_query) {
+		g_object_ref (priv->cached_all_query);
+		query_model = priv->cached_all_query;
+	} else {
+		GPtrArray *query;
+	
+		query_model = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
+					    "db", db,
+					    "max-count", priv->limit_count,
+					    "max-size", priv->limit_mb,
+					    "max-time", priv->limit_time, 
+					    NULL);
+
+		query = construct_query_from_selection (source);
+		rhythmdb_do_full_query_async_parsed (db, GTK_TREE_MODEL (query_model), query);
+		rhythmdb_query_free (query);
+	}
+
+	rb_debug ("setting query model");
+	rb_entry_view_set_model (rb_source_get_entry_view (RB_SOURCE (source)), query_model);
+	g_object_set (RB_SOURCE (source), "query-model", query_model, NULL);
+
+	g_object_unref (G_OBJECT (query_model));
+}
 void
 rb_auto_playlist_source_set_query (RBAutoPlaylistSource *source,
 				    GPtrArray *query,
@@ -403,11 +519,14 @@
 				    gint sort_direction)
 {
 	RBAutoPlaylistSourcePrivate *priv = RB_AUTO_PLAYLIST_SOURCE_GET_PRIVATE (source);
+	RhythmDB *db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source));
 	RBEntryView *songs = rb_source_get_entry_view (RB_SOURCE (source));
 
 	priv->query_resetting = TRUE;
 	if (priv->query)
 		rhythmdb_query_free (priv->query);
+	if (priv->cached_all_query)
+		g_object_unref (G_OBJECT (priv->cached_all_query));
 
 	/* playlists that aren't limited, with a particular sort order, are user-orderable */
 	rb_entry_view_set_columns_clickable (songs, (limit_count == 0 && limit_mb == 0));
@@ -417,7 +536,17 @@
 	priv->limit_count = limit_count;
 	priv->limit_mb = limit_mb;
 	priv->limit_time = limit_time;
+	
+	priv->cached_all_query = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
+					      "db", db,
+					      "max-count", priv->limit_count,
+					      "max-size", priv->limit_mb,
+					      "max-time", priv->limit_time, 
+					      NULL);
+	rhythmdb_do_full_query_async_parsed (db, GTK_TREE_MODEL (priv->cached_all_query), query);
+
 	rb_auto_playlist_source_do_query (source);
+	rb_library_browser_set_model (priv->browser, priv->cached_all_query);
 
 	priv->query_resetting = FALSE;
 }
@@ -455,3 +584,9 @@
 	rb_entry_view_resort_model (view);
 }
 
+static void
+rb_auto_playlist_source_browser_changed_cb (RBLibraryBrowser *entry, RBAutoPlaylistSource *source)
+{
+	rb_auto_playlist_source_do_query (source);
+	rb_source_notify_filter_changed (RB_SOURCE (source));
+}
Index: sources/rb-library-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-library-source.c,v
retrieving revision 1.141
diff -u -u -r1.141 rb-library-source.c
--- sources/rb-library-source.c	15 Jan 2006 02:13:15 -0000	1.141
+++ sources/rb-library-source.c	7 Feb 2006 11:25:47 -0000
@@ -33,12 +33,11 @@
 #include "rb-library-source.h"
 
 #include "rhythmdb-query-model.h"
-#include "rhythmdb-property-model.h"
 #include "rb-property-view.h"
 #include "rb-glade-helpers.h"
 #include "rb-stock-icons.h"
 #include "rb-entry-view.h"
-#include "rb-property-view.h"
+#include "rb-library-browser.h"
 #include "rb-util.h"
 #include "rb-file-helpers.h"
 #include "rb-dialog.h"
@@ -49,15 +48,6 @@
 #include "rb-preferences.h"
 #include "rb-shell-preferences.h"
 
-typedef enum
-{
-	RB_LIBRARY_QUERY_TYPE_ALL,
-	RB_LIBRARY_QUERY_TYPE_GENRE,
-	RB_LIBRARY_QUERY_TYPE_ARTIST,
-	RB_LIBRARY_QUERY_TYPE_ALBUM,
-	RB_LIBRARY_QUERY_TYPE_SEARCH,
-} RBLibraryQueryType;
-
 static void rb_library_source_class_init (RBLibrarySourceClass *klass);
 static void rb_library_source_init (RBLibrarySource *source);
 static GObject *rb_library_source_constructor (GType type, guint n_construct_properties,
@@ -78,22 +68,11 @@
 						 RBShell *shell);
 static void rb_library_source_cmd_choose_album (GtkAction *action,
 						RBShell *shell);
-static void rb_library_source_handle_genre_selection (RBLibrarySource *libsource, GList *genres);
-static void rb_library_source_handle_artist_selection (RBLibrarySource *libsource, GList *artists);
-static void rb_library_source_handle_album_selection (RBLibrarySource *libsource, GList *albums);
-
-static void genres_selected_cb (RBPropertyView *propview, GList *genres,
-			       RBLibrarySource *libsource);
-static void genres_selection_reset_cb (RBPropertyView *propview, RBLibrarySource *libsource);
-static void artists_selected_cb (RBPropertyView *propview, GList *artists,
-			       RBLibrarySource *libsource);
-static void artists_selection_reset_cb (RBPropertyView *propview, RBLibrarySource *libsource);
-static void albums_selected_cb (RBPropertyView *propview, GList *albums,
-			       RBLibrarySource *libsource);
-static void albums_selection_reset_cb (RBPropertyView *propview, RBLibrarySource *libsource);
 static void songs_view_sort_order_changed_cb (RBEntryView *view, RBLibrarySource *source);
 static void rb_library_source_library_location_cb (GtkFileChooser *chooser,
 						   RBLibrarySource *source);
+static void rb_library_source_browser_changed_cb (RBLibraryBrowser *entry,
+						  RBLibrarySource *source);
 
 static void paned_size_allocate_cb (GtkWidget *widget,
 				    GtkAllocation *allocation,
@@ -106,10 +85,6 @@
 					       guint cnxn_id,
 					       GConfEntry *entry,
 					       RBLibrarySource *source); 
-static void rb_library_source_browser_views_changed (GConfClient *client,
-						     guint cnxn_id,
-						     GConfEntry *entry,
-						     RBLibrarySource *source);
 static void rb_library_source_library_location_changed (GConfClient *client,
 							guint cnxn_id,
 							GConfEntry *entry,
@@ -123,7 +98,7 @@
 static const char *impl_get_browser_key (RBSource *source);
 static GdkPixbuf *impl_get_pixbuf (RBSource *source);
 static RBEntryView *impl_get_entry_view (RBSource *source);
-static GList *impl_get_extra_views (RBSource *source);
+static GList *impl_get_property_views (RBSource *source);
 static void impl_delete (RBSource *source);
 static void impl_move_to_trash (RBSource *source);
 static void impl_search (RBSource *source, const char *text);
@@ -133,7 +108,7 @@
 static gboolean impl_receive_drag (RBSource *source, GtkSelectionData *data);
 static gboolean impl_show_popup (RBSource *source);
 static const char * impl_get_paned_key (RBLibrarySource *source);
-static void rb_library_source_do_query (RBLibrarySource *source, RBLibraryQueryType qtype);
+static void rb_library_source_do_query (RBLibrarySource *source);
 
 void rb_library_source_browser_views_activated_cb (GtkWidget *widget,
 						 RBLibrarySource *source);
@@ -149,7 +124,6 @@
 
 
 #define CONF_UI_LIBRARY_DIR CONF_PREFIX "/ui/library"
-#define CONF_UI_LIBRARY_BROWSER_VIEWS CONF_PREFIX "/ui/library/browser_views"
 #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"
@@ -159,14 +133,12 @@
 {
 	RhythmDB *db;
 	
-	GtkWidget *browser;
+	RBLibraryBrowser *browser;
 	GtkWidget *vbox;
+	GSList *browser_views_group;
 
 	GdkPixbuf *pixbuf;
 
-	RBPropertyView *genres;
-	RBPropertyView *artists;
-	RBPropertyView *albums;
 	RBEntryView *songs;
 
 	GtkWidget *paned;
@@ -174,22 +146,13 @@
 	gboolean lock;
 
 	RhythmDBQueryModel *cached_all_query;
-	RhythmDBPropertyModel *cached_genres_model;
-	RhythmDBPropertyModel *cached_artists_model;
-	RhythmDBPropertyModel *cached_albums_model;
-	
-	RhythmDBQueryModel *model;
 	char *search_text;
-	GList *selected_genres;
-	GList *selected_artists;
-	GList *selected_albums;
 	
 	gboolean loading_prefs;
 	RBShellPreferences *shell_prefs;
 
 	GtkActionGroup *action_group;
 	GtkWidget *config_widget;
-	GSList *browser_views_group;
 
 	GtkFileChooser *library_location_widget;
 	GtkWidget *watch_library_check;
@@ -202,7 +165,6 @@
 	guint state_paned_notify_id;
 	guint state_browser_notify_id;
 	guint state_sorting_notify_id;
-	guint browser_view_notify_id;
 	guint library_location_notify_id;
 };
 
@@ -253,7 +215,7 @@
 	source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_search = impl_search;
 	source_class->impl_get_entry_view = impl_get_entry_view;
-	source_class->impl_get_extra_views = impl_get_extra_views;
+	source_class->impl_get_property_views = impl_get_property_views;
 	source_class->impl_get_config_widget = impl_get_config_widget;
 	source_class->impl_reset_filters = impl_reset_filters;
 	source_class->impl_song_properties = impl_song_properties;
@@ -302,81 +264,6 @@
 }
 
 static void
-update_browser_views_visibility (RBLibrarySource *source)
-{
-	int views;
-	GtkWidget *genres = GTK_WIDGET (source->priv->genres);
-	GtkWidget *artists = GTK_WIDGET (source->priv->artists);
-	GtkWidget *albums = GTK_WIDGET (source->priv->albums);
-	GtkAction *action;
-
-	views = eel_gconf_get_integer (CONF_UI_LIBRARY_BROWSER_VIEWS);
-
-	gtk_widget_show (genres);
-	action = gtk_action_group_get_action (source->priv->action_group,
-					      "LibrarySrcChooseGenre");
-	if (action) {
-		g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL);
-	}
-
-
-	gtk_widget_show (artists);	
-	action = gtk_action_group_get_action (source->priv->action_group,
-					      "LibrarySrcChooseArtist"); 
-	if (action) {
-		g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL);
-	}
-
-	gtk_widget_show (albums);
-	action = gtk_action_group_get_action (source->priv->action_group,
-					      "LibrarySrcChooseAlbum"); 
-	if (action) {
-		g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL);
-	}
-	
-	switch (views)
-	{
-	case 0:
-		action = gtk_action_group_get_action (source->priv->action_group,
-						      "LibrarySrcChooseGenre");
-		if (action) {
-			g_object_set (G_OBJECT (action), 
-				      "sensitive", FALSE, 
-				      NULL);
-		}
-		gtk_widget_hide (genres);
-		rb_library_source_handle_genre_selection (source, NULL);
-		/* Since this is hidden now, reset the query */
-		break;
-	case 1:
-		action = gtk_action_group_get_action (source->priv->action_group,
-						      "LibrarySrcChooseAlbum");
-		if (action) {
-			g_object_set (G_OBJECT (action), 
-				      "sensitive", FALSE, 
-				      NULL);
-		}
-		gtk_widget_hide (albums);
-		rb_library_source_handle_album_selection (source, NULL);
-		break;
-	case 2:
-		/* All are shown */
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-rb_library_source_browser_views_changed (GConfClient *client,
-					 guint cnxn_id,
-					 GConfEntry *entry,
-					 RBLibrarySource *source)
-{
-	update_browser_views_visibility (source);
-}
-
-static void
 rb_library_source_library_location_changed (GConfClient *client,
 					    guint cnxn_id,
 					    GConfEntry *entry,
@@ -452,7 +339,6 @@
 	rb_debug ("finalizing library source");
 	
 	eel_gconf_notification_remove (source->priv->ui_dir_notify_id);
-	eel_gconf_notification_remove (source->priv->browser_view_notify_id);
 	eel_gconf_notification_remove (source->priv->library_location_notify_id);
 	eel_gconf_notification_remove (source->priv->state_browser_notify_id);
 	eel_gconf_notification_remove (source->priv->state_paned_notify_id);
@@ -534,7 +420,11 @@
 
 	source->priv->paned = gtk_vpaned_new ();
 
-	source->priv->browser = gtk_hbox_new (TRUE, 5);
+	source->priv->browser = rb_library_browser_new (source->priv->db);
+	gtk_paned_pack1 (GTK_PANED (source->priv->paned), GTK_WIDGET (source->priv->browser), TRUE, FALSE);
+	g_signal_connect_object (G_OBJECT (source->priv->browser), "changed",
+				 G_CALLBACK (rb_library_source_browser_changed_cb),
+				 source, 0);
 
 	/* set up songs tree view */
 	source->priv->songs = rb_entry_view_new (source->priv->db, shell_player,
@@ -579,48 +469,6 @@
 				   GDK_ACTION_COPY | GDK_ACTION_MOVE);	/* really accept move actions? */
 	}
 
-	/* set up genres treeview */
-	source->priv->genres = rb_property_view_new (source->priv->db, RHYTHMDB_PROP_GENRE,
-						     _("Genre"));
-	rb_property_view_set_selection_mode (source->priv->genres, GTK_SELECTION_MULTIPLE);
-	g_signal_connect_object (G_OBJECT (source->priv->genres),
-				 "properties-selected",
-				 G_CALLBACK (genres_selected_cb),
-				 source, 0);
-	g_signal_connect_object (G_OBJECT (source->priv->genres),
-				 "property-selection-reset", G_CALLBACK (genres_selection_reset_cb),
-				 source, 0);
-
-	gtk_box_pack_start_defaults (GTK_BOX (source->priv->browser), GTK_WIDGET (source->priv->genres));
-
-	/* set up artist treeview */
-	source->priv->artists = rb_property_view_new (source->priv->db, RHYTHMDB_PROP_ARTIST,
-						      _("Artist"));
-	rb_property_view_set_selection_mode (source->priv->artists, GTK_SELECTION_MULTIPLE);
-	g_signal_connect_object (G_OBJECT (source->priv->artists),
-				 "properties-selected",
-				 G_CALLBACK (artists_selected_cb),
-				 source, 0);
-	g_signal_connect_object (G_OBJECT (source->priv->artists),
-				 "property-selection-reset", G_CALLBACK (artists_selection_reset_cb),
-				 source, 0);
-
-	gtk_box_pack_start_defaults (GTK_BOX (source->priv->browser), GTK_WIDGET (source->priv->artists));
-
-	/* set up albums treeview */
-	source->priv->albums = rb_property_view_new (source->priv->db, RHYTHMDB_PROP_ALBUM,
-						     _("Album"));
-	rb_property_view_set_selection_mode (source->priv->albums, GTK_SELECTION_MULTIPLE);
-	g_signal_connect_object (G_OBJECT (source->priv->albums),
-				 "properties-selected",
-				 G_CALLBACK (albums_selected_cb),
-				 source, 0);
-	g_signal_connect_object (G_OBJECT (source->priv->albums),
-				 "property-selection-reset", G_CALLBACK (albums_selection_reset_cb),
-				 source, 0);
-
-	gtk_box_pack_start_defaults (GTK_BOX (source->priv->browser), GTK_WIDGET (source->priv->albums));
-	gtk_paned_pack1 (GTK_PANED (source->priv->paned), source->priv->browser, FALSE, FALSE);
 
 	/* this gets emitted when the paned thingie is moved */
 	g_signal_connect_object (G_OBJECT (source->priv->songs),
@@ -636,7 +484,6 @@
 
 	rb_library_source_state_prefs_sync (source);
 	rb_library_source_ui_prefs_sync (source);
-	update_browser_views_visibility (source);
 
 	if (source->priv->sorting_key) {
 		source->priv->state_sorting_notify_id =
@@ -660,14 +507,18 @@
 	source->priv->ui_dir_notify_id =
 		eel_gconf_notification_add (CONF_UI_LIBRARY_DIR,
 				    (GConfClientNotifyFunc) rb_library_source_ui_pref_changed, source);
-	source->priv->browser_view_notify_id =
-		eel_gconf_notification_add (CONF_UI_LIBRARY_BROWSER_VIEWS,
-				    (GConfClientNotifyFunc) rb_library_source_browser_views_changed, source);
 	source->priv->library_location_notify_id =
 		eel_gconf_notification_add (CONF_LIBRARY_LOCATION,
 				    (GConfClientNotifyFunc) rb_library_source_library_location_changed, source);
 
-	rb_library_source_do_query (source, RB_LIBRARY_QUERY_TYPE_ALL);
+	source->priv->cached_all_query = rhythmdb_query_model_new_empty (source->priv->db);
+	rhythmdb_do_full_query_async (source->priv->db,
+				      GTK_TREE_MODEL (source->priv->cached_all_query),
+				      RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, source->priv->entry_type,
+				      RHYTHMDB_QUERY_END);
+	rb_library_browser_set_model (source->priv->browser, source->priv->cached_all_query);
+	rb_library_source_do_query (source);
+
 	return G_OBJECT (source);
 }
 
@@ -753,82 +604,6 @@
 	return source;
 }
 
-static gboolean
-string_list_equal (GList *a, GList *b)
-{
-	GList *sorted_a_keys;
-	GList *sorted_b_keys;
-	GList *a_ptr, *b_ptr;
-	gboolean ret = TRUE;
-
-	if (g_list_length (a) != g_list_length (b))
-		return FALSE;
-
-	for (sorted_a_keys = NULL; a; a = a->next) {
-		sorted_a_keys = g_list_prepend (sorted_a_keys,
-					       g_utf8_collate_key (a->data, -1));
-	}
-	for (sorted_b_keys = NULL; b; b = b->next) {
-		sorted_b_keys = g_list_prepend (sorted_b_keys,
-					       g_utf8_collate_key (b->data, -1));
-	}
-	sorted_a_keys = g_list_sort (sorted_a_keys, (GCompareFunc) strcmp);
-	sorted_b_keys = g_list_sort (sorted_b_keys, (GCompareFunc) strcmp);
-	
-	for (a_ptr = sorted_a_keys, b_ptr = sorted_b_keys;
-	     a_ptr && b_ptr; a_ptr = a_ptr->next, b_ptr = b_ptr->next) {
-		if (strcmp (a_ptr->data, b_ptr->data)) {
-			ret = FALSE;
-			break;
-		}
-	}
-	g_list_foreach (sorted_a_keys, (GFunc) g_free, NULL);
-	g_list_foreach (sorted_b_keys, (GFunc) g_free, NULL);
-	g_list_free (sorted_a_keys);
-	g_list_free (sorted_b_keys);
-	return ret;
-}
-
-
-
-static void
-rb_library_source_gather_hash_keys (char *key, gpointer unused,
-				    GList **data)
-{
-	*data = g_list_prepend (*data, key);
-}
-
-static void
-rb_library_source_free_list_data (gpointer *data, gpointer unused)
-{
-	g_free (data);
-}
-
-static GList *
-rb_library_source_gather_properties (RBLibrarySource *source,
-				     RhythmDBPropType prop)
-{
-	GList *selected, *tem;
-	GHashTable *selected_set;
-
-	selected_set = g_hash_table_new (g_str_hash, g_str_equal);
-	selected = rb_entry_view_get_selected_entries (source->priv->songs);
-	for (tem = selected; tem; tem = tem->next) {
-		RhythmDBEntry *entry = tem->data;
-		char *val = g_strdup (rhythmdb_entry_get_string (entry, prop));
-		g_hash_table_insert (selected_set, val, NULL);
-	}
-
-
-	g_list_free (selected);
-	
-	tem = NULL;
-	g_hash_table_foreach (selected_set, (GHFunc) rb_library_source_gather_hash_keys,
-			      &tem);
-	g_hash_table_destroy (selected_set);
-	return tem;
-}
-
 static void
 rb_library_source_cmd_choose_genre (GtkAction *action,
 				    RBShell *shell)
@@ -839,10 +614,9 @@
 	rb_debug ("choosing genre");
 
 	g_object_get (G_OBJECT (shell), "selected-source", &source, NULL);
-	props = rb_library_source_gather_properties (source, RHYTHMDB_PROP_GENRE);
-	rb_property_view_set_selection (source->priv->genres, props);
-	g_list_foreach (props, (GFunc) rb_library_source_free_list_data, NULL);
-	g_list_free (props);
+	props = rb_source_gather_selected_properties (RB_SOURCE (source), RHYTHMDB_PROP_GENRE);
+	rb_library_browser_set_selection (source->priv->browser, RHYTHMDB_PROP_GENRE, props);
+	rb_list_deep_free (props);
 	g_object_unref (source);
 }
 
@@ -856,10 +630,9 @@
 	rb_debug ("choosing artist");
 
 	g_object_get (G_OBJECT (shell), "selected-source", &source, NULL);
-	props = rb_library_source_gather_properties (source, RHYTHMDB_PROP_ARTIST);
-	rb_property_view_set_selection (source->priv->artists, props);
-	g_list_foreach (props, (GFunc) rb_library_source_free_list_data, NULL);
-	g_list_free (props);
+	props = rb_source_gather_selected_properties (RB_SOURCE (source), RHYTHMDB_PROP_ARTIST);
+	rb_library_browser_set_selection (source->priv->browser, RHYTHMDB_PROP_ARTIST, props);
+	rb_list_deep_free (props);
 	g_object_unref (source);
 }
 
@@ -873,108 +646,13 @@
 	rb_debug ("choosing album");
 
 	g_object_get (G_OBJECT (shell), "selected-source", &source, NULL);
-	props = rb_library_source_gather_properties (source, RHYTHMDB_PROP_ALBUM);
-	rb_property_view_set_selection (source->priv->albums, props);
-	g_list_foreach (props, (GFunc) rb_library_source_free_list_data, NULL);
-	g_list_free (props);
+	props = rb_source_gather_selected_properties (RB_SOURCE (source), RHYTHMDB_PROP_ALBUM);
+	rb_library_browser_set_selection (source->priv->browser, RHYTHMDB_PROP_ALBUM, props);
+	rb_list_deep_free (props);
 	g_object_unref (source);
 }
 
 static void
-genres_selected_cb (RBPropertyView *propview, GList *genres,
-		   RBLibrarySource *libsource)
-{
-	rb_debug ("genre selected"); 
-	rb_library_source_handle_genre_selection (libsource, genres);
-}
-
-static void
-rb_library_source_handle_genre_selection (RBLibrarySource *libsource, GList *genres)
-{
-	if (string_list_equal (libsource->priv->selected_genres, genres))
-		return;
-	g_list_foreach (libsource->priv->selected_genres, (GFunc) g_free, NULL);
-	g_list_free (libsource->priv->selected_genres);
-	libsource->priv->selected_genres = genres;
-	rb_library_source_do_query (libsource, RB_LIBRARY_QUERY_TYPE_GENRE);
-
-	rb_source_notify_filter_changed (RB_SOURCE (libsource));
-}
-
-static void
-genres_selection_reset_cb (RBPropertyView *propview, RBLibrarySource *libsource)
-{
-	rb_debug ("genre selection reset"); 
-	g_list_foreach (libsource->priv->selected_genres, (GFunc) g_free, NULL);
-	g_list_free (libsource->priv->selected_genres);
-	libsource->priv->selected_genres = NULL;
-	rb_library_source_do_query (libsource, RB_LIBRARY_QUERY_TYPE_GENRE);
-}
-
-static void
-artists_selected_cb (RBPropertyView *propview, GList *artists, 
-		     RBLibrarySource *libsource)
-{
-	rb_library_source_handle_artist_selection (libsource, artists);
-}
-
-static void
-rb_library_source_handle_artist_selection (RBLibrarySource *libsource, GList *artists)
-{
-
-	rb_debug ("artist selected"); 
-	if (string_list_equal (libsource->priv->selected_artists, artists))
-		return;
-	g_list_foreach (libsource->priv->selected_artists, (GFunc) g_free, NULL);
-	g_list_free (libsource->priv->selected_artists);
-	libsource->priv->selected_artists = artists;
-	rb_library_source_do_query (libsource, RB_LIBRARY_QUERY_TYPE_ARTIST);
-
-	rb_source_notify_filter_changed (RB_SOURCE (libsource));
-}
-
-static void
-artists_selection_reset_cb (RBPropertyView *propview, RBLibrarySource *libsource)
-{
-	rb_debug ("artist selection reset"); 
-	g_list_foreach (libsource->priv->selected_artists, (GFunc) g_free, NULL);
-	g_list_free (libsource->priv->selected_artists);
-	libsource->priv->selected_artists = NULL;
-	rb_library_source_do_query (libsource, RB_LIBRARY_QUERY_TYPE_ALBUM);
-}
-
-static void
-albums_selected_cb (RBPropertyView *propview, GList *albums, 
-		    RBLibrarySource *libsource)
-{
-	rb_debug ("album selected"); 
-	rb_library_source_handle_album_selection (libsource, albums);
-}
-
-static void
-rb_library_source_handle_album_selection (RBLibrarySource *libsource, GList *albums)
-{
-	if (string_list_equal (libsource->priv->selected_albums, albums))
-		return;
-	g_list_foreach (libsource->priv->selected_albums, (GFunc) g_free, NULL);
-	g_list_free (libsource->priv->selected_albums);
-	libsource->priv->selected_albums = albums;
-	rb_library_source_do_query (libsource, RB_LIBRARY_QUERY_TYPE_ALBUM);
-
-	rb_source_notify_filter_changed (RB_SOURCE (libsource));
-}
-
-static void
-albums_selection_reset_cb (RBPropertyView *propview, RBLibrarySource *libsource)
-{
-	rb_debug ("album selection reset"); 
-	g_list_foreach (libsource->priv->selected_albums, (GFunc) g_free, NULL);
-	g_list_free (libsource->priv->selected_albums);
-	libsource->priv->selected_albums = NULL;
-	rb_library_source_do_query (libsource, RB_LIBRARY_QUERY_TYPE_ALBUM);
-}
-
-static void
 songs_view_sort_order_changed_cb (RBEntryView *view, RBLibrarySource *source)
 {
 	rb_debug ("sort order changed");
@@ -1020,7 +698,7 @@
 
 	g_free (source->priv->search_text);
 	source->priv->search_text = g_strdup (search_text);
-	rb_library_source_do_query (source, RB_LIBRARY_QUERY_TYPE_SEARCH);
+	rb_library_source_do_query (source);
 
 	rb_source_notify_filter_changed (RB_SOURCE (source));
 }
@@ -1035,14 +713,12 @@
 }
 
 static GList *
-impl_get_extra_views (RBSource *asource)
+impl_get_property_views (RBSource *asource)
 {
 	GList *ret;
 	RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
 
-	ret = g_list_prepend (NULL, source->priv->albums);
-	ret = g_list_prepend (ret, source->priv->artists);
-	ret = g_list_prepend (ret, source->priv->genres);
+	ret =  rb_library_browser_get_property_views (source->priv->browser);
 	return ret;
 }
 
@@ -1052,23 +728,9 @@
 	RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
 	gboolean changed = FALSE;
 
-	if (source->priv->selected_genres != NULL)
-		changed = TRUE;
-	g_list_foreach (source->priv->selected_genres, (GFunc) g_free, NULL);
-	g_list_free (source->priv->selected_genres);
-	source->priv->selected_genres = NULL;
-
-	if (source->priv->selected_artists != NULL)
-		changed = TRUE;
-	g_list_foreach (source->priv->selected_artists, (GFunc) g_free, NULL);
-	g_list_free (source->priv->selected_artists);
-	source->priv->selected_artists = NULL;
-
-	if (source->priv->selected_albums != NULL)
+	
+	if (rb_library_browser_reset (source->priv->browser))
 		changed = TRUE;
-	g_list_foreach (source->priv->selected_albums, (GFunc) g_free, NULL);
-	g_list_free (source->priv->selected_albums);
-	source->priv->selected_albums = NULL;
 
 	if (source->priv->search_text != NULL)
 		changed = TRUE;
@@ -1076,7 +738,7 @@
 	source->priv->search_text = NULL;
 
 	if (changed) {
-		rb_library_source_do_query (source, RB_LIBRARY_QUERY_TYPE_ALL);
+		rb_library_source_do_query (source);
 		rb_source_notify_filter_changed (RB_SOURCE (source));
 	}
 }
@@ -1135,7 +797,7 @@
 	rb_debug ("syncing pref dialog state");
 
 	list = g_slist_nth (source->priv->browser_views_group,
-			    eel_gconf_get_integer (CONF_UI_LIBRARY_BROWSER_VIEWS));
+			    eel_gconf_get_integer (CONF_UI_BROWSER_VIEWS));
 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (list->data), TRUE);
 
 	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
@@ -1170,7 +832,7 @@
 
 	index = g_slist_index (source->priv->browser_views_group, widget);
 
-	eel_gconf_set_integer (CONF_UI_LIBRARY_BROWSER_VIEWS, index);
+	eel_gconf_set_integer (CONF_UI_BROWSER_VIEWS, index);
 }
 
 static void
@@ -1246,9 +908,9 @@
 
 	key = rb_source_get_browser_key (RB_SOURCE (source));
 	if (key && eel_gconf_get_boolean (key))
-			gtk_widget_show (source->priv->browser);
+			gtk_widget_show (GTK_WIDGET (source->priv->browser));
 		else
-			gtk_widget_hide (source->priv->browser);
+			gtk_widget_hide (GTK_WIDGET (source->priv->browser));
 }
 
 static void
@@ -1406,43 +1068,12 @@
 	return klass->impl_has_drop_support (source);
 }
 
-
-static void
-push_multi_equals_query (RhythmDB *db, GPtrArray *query, guint propid, GList *items)
-{
-	GPtrArray *subquery = g_ptr_array_new ();
-
-	if (!items) {
-		g_ptr_array_free (subquery, TRUE);
-		return;
-	}
-
-	rhythmdb_query_append (db,
-			       subquery,
-			       RHYTHMDB_QUERY_PROP_EQUALS,
-			       propid,
-			       items->data,
-			       RHYTHMDB_QUERY_END);
-	items = items->next;
-	while (items) {
-		rhythmdb_query_append (db,
-				       subquery,
-				       RHYTHMDB_QUERY_DISJUNCTION,
-				       RHYTHMDB_QUERY_PROP_EQUALS,
-				       propid,
-				       items->data,
-				       RHYTHMDB_QUERY_END);
-		items = items->next;
-	}
-	rhythmdb_query_append (db, query, RHYTHMDB_QUERY_SUBQUERY, subquery,
-			       RHYTHMDB_QUERY_END);
-}
-
 static GPtrArray *
 construct_query_from_selection (RBLibrarySource *source)
 {
 	GPtrArray *query;
 	RhythmDBEntryType entry_type;
+	GPtrArray *browser_query;
 
 	g_object_get (G_OBJECT (source), "entry-type", &entry_type, NULL);
 	query = rhythmdb_query_parse (source->priv->db,
@@ -1470,169 +1101,41 @@
 		 */
 	}
 
-	if (source->priv->selected_genres) {
-		if (source->priv->selected_genres->next == NULL)
-			rhythmdb_query_append (source->priv->db,
-					       query,
-					       RHYTHMDB_QUERY_PROP_EQUALS,
-					       RHYTHMDB_PROP_GENRE,
-					       source->priv->selected_genres->data,
-					       RHYTHMDB_QUERY_END);
-		else 
-			push_multi_equals_query (source->priv->db,
-						 query,
-						 RHYTHMDB_PROP_GENRE,
-						 source->priv->selected_genres);
-	}
-
-	if (source->priv->selected_artists) {
-		if (source->priv->selected_artists->next == NULL)
-			rhythmdb_query_append (source->priv->db,
-					       query,
-					       RHYTHMDB_QUERY_PROP_EQUALS,
-					       RHYTHMDB_PROP_ARTIST,
-					       source->priv->selected_artists->data,
-					       RHYTHMDB_QUERY_END);
-		else 
-			push_multi_equals_query (source->priv->db,
-						 query,
-						 RHYTHMDB_PROP_ARTIST,
-						 source->priv->selected_artists);
-	}
+	
+	browser_query = rb_library_browser_construct_query (source->priv->browser);
+	rhythmdb_query_concatenate (query, browser_query);
+	rhythmdb_query_free (browser_query);
 
-	if (source->priv->selected_albums) {
-		if (source->priv->selected_albums->next == NULL)
-			rhythmdb_query_append (source->priv->db,
-					       query,
-					       RHYTHMDB_QUERY_PROP_EQUALS,
-					       RHYTHMDB_PROP_ALBUM,
-					       source->priv->selected_albums->data,
-					       RHYTHMDB_QUERY_END);
-		else 
-			push_multi_equals_query (source->priv->db,
-						 query,
-						 RHYTHMDB_PROP_ALBUM,
-						 source->priv->selected_albums);
-	}
 	return query;
 }
 
 static void
-rb_library_source_do_query (RBLibrarySource *source, RBLibraryQueryType qtype)
+rb_library_source_do_query (RBLibrarySource *source)
 {
 	RhythmDBQueryModel *query_model;
-	RhythmDBPropertyModel *genre_model = NULL;
-	RhythmDBPropertyModel *artist_model = NULL;
-	RhythmDBPropertyModel *album_model = NULL;
-	GPtrArray *query;
 	gboolean is_all_query;
-	gboolean do_new_query = TRUE;
 
-	is_all_query = (source->priv->selected_genres == NULL &&
-			source->priv->selected_artists == NULL &&	    
-			source->priv->selected_albums == NULL &&	    
-			source->priv->search_text == NULL);
-
-	if ((source->priv->cached_all_query == NULL) || is_all_query ) {
-		if (source->priv->cached_all_query == NULL) {
-			rb_debug ("caching new query");
-			query_model = source->priv->model = 
-				source->priv->cached_all_query = rhythmdb_query_model_new_empty (source->priv->db);
-			source->priv->cached_genres_model = rhythmdb_property_model_new (source->priv->db, RHYTHMDB_PROP_GENRE);
-			source->priv->cached_artists_model = rhythmdb_property_model_new (source->priv->db, RHYTHMDB_PROP_ARTIST);
-			source->priv->cached_albums_model = rhythmdb_property_model_new (source->priv->db, RHYTHMDB_PROP_ALBUM);
-		} else {
-			rb_debug ("using cached query");
-			query_model = source->priv->model = source->priv->cached_all_query;
-			do_new_query = FALSE;
-		}
-		
-		g_object_set (G_OBJECT (source->priv->cached_genres_model),
-			      "query-model", source->priv->cached_all_query, NULL);
-		g_object_set (G_OBJECT (source->priv->cached_artists_model),
-			      "query-model", source->priv->cached_all_query, NULL);
-		g_object_set (G_OBJECT (source->priv->cached_albums_model),
-			      "query-model", source->priv->cached_all_query, NULL);
-		rb_property_view_set_model (source->priv->genres,
-					    source->priv->cached_genres_model);
-		rb_property_view_set_model (source->priv->artists,
-					    source->priv->cached_artists_model);
-		rb_property_view_set_model (source->priv->albums,
-					    source->priv->cached_albums_model);
+	is_all_query = (!rb_library_browser_has_selection (source->priv->browser) &&
+			(source->priv->search_text == NULL));
+
+	if (is_all_query) {
+		g_object_ref (source->priv->cached_all_query);
+		query_model = source->priv->cached_all_query;
 	} else {
-		rb_debug ("query is not special");
-		source->priv->model = 
-			query_model = rhythmdb_query_model_new_empty (source->priv->db);
-
-		if (source->priv->selected_genres == NULL) {
-			rb_property_view_set_model (source->priv->genres,
-						    source->priv->cached_genres_model);
-			if (source->priv->selected_artists == NULL) {
-				rb_property_view_set_model (source->priv->artists,
-							    source->priv->cached_artists_model);
-				if (source->priv->selected_albums == NULL)
-					rb_property_view_set_model (source->priv->albums,
-								    source->priv->cached_albums_model);
-			}
-		}
-		switch (qtype) {
-		case RB_LIBRARY_QUERY_TYPE_ALL:
-			rb_debug ("resetting genres view");
-			g_list_foreach (source->priv->selected_genres,
-					(GFunc) g_free, NULL);
-			g_list_free (source->priv->selected_genres);
-			source->priv->selected_genres = NULL;
-
-			rb_property_view_reset (source->priv->genres);
-			genre_model = rb_property_view_get_model (source->priv->genres);
-			g_object_set (G_OBJECT (genre_model), "query-model",
-				      query_model, NULL);
-			g_object_unref (G_OBJECT (genre_model));
-		case RB_LIBRARY_QUERY_TYPE_GENRE:
-			rb_debug ("resetting artist view");
-			g_list_foreach (source->priv->selected_artists,
-					(GFunc) g_free, NULL);
-			g_list_free (source->priv->selected_artists);
-			source->priv->selected_artists = NULL;
-
-			rb_property_view_reset (source->priv->artists);
-			artist_model = rb_property_view_get_model (source->priv->artists);
-			g_object_set (G_OBJECT (artist_model), "query-model",
-				      query_model, NULL);
-			g_object_unref (G_OBJECT (artist_model));
-		case RB_LIBRARY_QUERY_TYPE_ARTIST:
-			rb_debug ("resetting album view");
-			g_list_foreach (source->priv->selected_albums,
-					(GFunc) g_free, NULL);
-			g_list_free (source->priv->selected_albums);
-			source->priv->selected_albums = NULL;
-
-			rb_property_view_reset (source->priv->albums);
-			album_model = rb_property_view_get_model (source->priv->albums);
-			g_object_set (G_OBJECT (album_model), "query-model",
-				      query_model, NULL);
-			g_object_unref (G_OBJECT (album_model));
-		case RB_LIBRARY_QUERY_TYPE_ALBUM:
-		case RB_LIBRARY_QUERY_TYPE_SEARCH:
-			break;
-		}
+		GPtrArray *query;
+	
+		query_model = rhythmdb_query_model_new_empty (source->priv->db);
+
+		query = construct_query_from_selection (source);
+		rhythmdb_do_full_query_async_parsed (source->priv->db, GTK_TREE_MODEL (query_model), query);
+		rhythmdb_query_free (query);
 	}
 
 	rb_debug ("setting query model");
 	rb_entry_view_set_model (source->priv->songs, query_model);
 	g_object_set (RB_SOURCE (source), "query-model", query_model, NULL);
 
-	if (do_new_query) {
-		query = construct_query_from_selection (source);
-		
-		rb_debug ("doing query");
-		rhythmdb_do_full_query_async_parsed (source->priv->db, GTK_TREE_MODEL (query_model), query);
-			
-		rhythmdb_query_free (query);
-	}
-	
-	if (!is_all_query)
-		g_object_unref (G_OBJECT (query_model));
+	g_object_unref (G_OBJECT (query_model));
 }
 
 static void
@@ -1716,3 +1219,9 @@
 	eel_gconf_set_boolean (CONF_MONITOR_LIBRARY, active);
 }
 
+static void
+rb_library_source_browser_changed_cb (RBLibraryBrowser *entry, RBLibrarySource *source)
+{
+	rb_library_source_do_query (source);
+	rb_source_notify_filter_changed (RB_SOURCE (source));
+}
Index: sources/rb-playlist-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-playlist-source.c,v
retrieving revision 1.105
diff -u -u -r1.105 rb-playlist-source.c
--- sources/rb-playlist-source.c	6 Feb 2006 10:38:44 -0000	1.105
+++ sources/rb-playlist-source.c	7 Feb 2006 11:25:48 -0000
@@ -106,8 +106,6 @@
 	RhythmDBEntryType entry_type;
 	RhythmDBQueryModel *model;
 
-	GtkWidget *vbox;
-
 	RBEntryView *songs;
 
 	gboolean dirty;
@@ -220,10 +218,6 @@
 	shell_player = rb_shell_get_player (shell);
 	g_object_unref (G_OBJECT (shell));
 
-	source->priv->vbox = gtk_vbox_new (FALSE, 5);
-
-	gtk_container_add (GTK_CONTAINER (source), source->priv->vbox);
-
 	source->priv->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
 	g_signal_connect_object (G_OBJECT (source->priv->db), "entry_added",
@@ -272,7 +266,7 @@
 
 	rb_playlist_source_setup_entry_view (source, source->priv->songs);
 
-	gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), GTK_WIDGET (source->priv->songs));
+	gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (source->priv->songs));
 		
 	gtk_widget_show_all (GTK_WIDGET (source));
 
Index: sources/rb-podcast-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-podcast-source.c,v
retrieving revision 1.48
diff -u -u -r1.48 rb-podcast-source.c
--- sources/rb-podcast-source.c	5 Feb 2006 05:28:31 -0000	1.48
+++ sources/rb-podcast-source.c	7 Feb 2006 11:25:48 -0000
@@ -1004,10 +1004,9 @@
 	        g_list_free (source->priv->selected_feeds);
 	}	
 
-	source->priv->selected_feeds = feeds;
+	source->priv->selected_feeds = rb_string_list_copy (feeds);
 	
 	rb_podcast_source_do_query (source, RB_PODCAST_QUERY_TYPE_ALBUM);
-
 	rb_source_notify_filter_changed (RB_SOURCE (source));
 }
 
Index: sources/rb-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-source.c,v
retrieving revision 1.53
diff -u -u -r1.53 rb-source.c
--- sources/rb-source.c	3 Feb 2006 09:31:09 -0000	1.53
+++ sources/rb-source.c	7 Feb 2006 11:25:49 -0000
@@ -47,7 +47,7 @@
 					GParamSpec *pspec);
 
 static const char * default_get_browser_key (RBSource *source);
-static GList *default_get_extra_views (RBSource *source);
+static GList *default_get_property_views (RBSource *source);
 static gboolean default_can_rename (RBSource *source);
 static gboolean default_can_search (RBSource *source);
 static GdkPixbuf *default_get_pixbuf (RBSource *source);
@@ -117,7 +117,8 @@
 
 	klass->impl_can_browse = (RBSourceFeatureFunc) rb_false_function;
 	klass->impl_get_browser_key = default_get_browser_key;
-	klass->impl_get_extra_views = default_get_extra_views;
+	klass->impl_browser_toggled = NULL;
+	klass->impl_get_property_views = default_get_property_views;
 	klass->impl_can_rename = default_can_rename;
 	klass->impl_can_search = default_can_search;
 	klass->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
@@ -406,6 +407,15 @@
 }
 
 void
+rb_source_browser_toggled (RBSource *source, gboolean enabled)
+{
+	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+	if (klass->impl_browser_toggled != NULL)
+		klass->impl_browser_toggled (source, enabled);
+}
+
+void
 rb_source_notify_status_changed (RBSource *source)
 {
 	g_signal_emit (G_OBJECT (source), rb_source_signals[STATUS_CHANGED], 0);
@@ -454,17 +464,17 @@
 }
 
 static GList *
-default_get_extra_views (RBSource *source)
+default_get_property_views (RBSource *source)
 {
 	return NULL;
 }
 
 GList *
-rb_source_get_extra_views (RBSource *source)
+rb_source_get_property_views (RBSource *source)
 {
 	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
 
-	return klass->impl_get_extra_views (source);
+	return klass->impl_get_property_views (source);
 }
 
 static GdkPixbuf *
@@ -850,6 +860,36 @@
 		priv->idle_status_changed_id = 
 			g_idle_add ((GSourceFunc) idle_emit_status_changed, source);
 	}
+}
+
+static void
+rb_source_gather_hash_keys (char *key, gpointer unused, GList **data)
+{
+	*data = g_list_prepend (*data, key);
+}
+
+GList *
+rb_source_gather_selected_properties (RBSource *source,
+				      RhythmDBPropType prop)
+{
+	GList *selected, *tem;
+	GHashTable *selected_set;
+
+	selected_set = g_hash_table_new (g_str_hash, g_str_equal);
+	selected = rb_entry_view_get_selected_entries (rb_source_get_entry_view (RB_SOURCE (source)));
+	for (tem = selected; tem; tem = tem->next) {
+		RhythmDBEntry *entry = tem->data;
+		char *val = g_strdup (rhythmdb_entry_get_string (entry, prop));
+		g_hash_table_insert (selected_set, val, NULL);
+	}
+
+	g_list_free (selected);
+	
+	tem = NULL;
+	g_hash_table_foreach (selected_set, (GHFunc) rb_source_gather_hash_keys,
+			      &tem);
+	g_hash_table_destroy (selected_set);
+	return tem;
 }
 
 
Index: sources/rb-source.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-source.h,v
retrieving revision 1.34
diff -u -u -r1.34 rb-source.h
--- sources/rb-source.h	3 Feb 2006 09:31:09 -0000	1.34
+++ sources/rb-source.h	7 Feb 2006 11:25:49 -0000
@@ -75,9 +75,10 @@
 
 	gboolean	(*impl_can_browse)	(RBSource *source);
 	const char *	(*impl_get_browser_key)	(RBSource *source);
+	void		(*impl_browser_toggled)	(RBSource *source, gboolean enabled);
 
 	RBEntryView *	(*impl_get_entry_view)	(RBSource *source);
-	GList *		(*impl_get_extra_views)	(RBSource *source);
+	GList *		(*impl_get_property_views)	(RBSource *source);
 
 	GdkPixbuf *	(*impl_get_pixbuf)	(RBSource *source);
 	gboolean	(*impl_can_rename)	(RBSource *source);
@@ -133,16 +134,16 @@
 
 gboolean	rb_source_can_browse		(RBSource *source);
 const char *	rb_source_get_browser_key	(RBSource *source);
+void		rb_source_browser_toggled	(RBSource *source, gboolean enabled);
 
 RBEntryView *	rb_source_get_entry_view	(RBSource *source);
 
-GList *		rb_source_get_extra_views	(RBSource *source);
+GList *		rb_source_get_property_views	(RBSource *source);
 
 GdkPixbuf *	rb_source_get_pixbuf		(RBSource *source);
 gboolean	rb_source_can_rename		(RBSource *source);
 
 gboolean	rb_source_can_search		(RBSource *source);
-
 void		rb_source_search		(RBSource *source,
 						 const char *text);
 
@@ -181,7 +182,10 @@
 void		rb_source_activate		(RBSource *source);
 void		rb_source_deactivate		(RBSource *source);
 gboolean	rb_source_disconnect		(RBSource *source);
+
 GList*		rb_source_get_ui_actions	(RBSource *source);
+
+GList *		rb_source_gather_selected_properties (RBSource *source, RhythmDBPropType prop);
 
 /* Protected method, should only be used by objects inheriting from RBSource */
 void            _rb_source_show_popup           (RBSource *source, 
Index: widgets/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/widgets/Makefile.am,v
retrieving revision 1.25
diff -u -u -r1.25 Makefile.am
--- widgets/Makefile.am	5 Feb 2006 05:28:32 -0000	1.25
+++ widgets/Makefile.am	7 Feb 2006 11:25:49 -0000
@@ -38,6 +38,8 @@
 	rb-rating.c					\
 	rb-rating.h					\
 	rb-rating-helper.c				\
+	rb-library-browser.h				\
+	rb-library-browser.c				\
 	rb-rating-helper.h				\
 	rb-query-creator.c				\
 	rb-query-creator.h				\
Index: widgets/rb-property-view.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/widgets/rb-property-view.c,v
retrieving revision 1.40
diff -u -u -r1.40 rb-property-view.c
--- widgets/rb-property-view.c	5 Feb 2006 05:28:32 -0000	1.40
+++ widgets/rb-property-view.c	7 Feb 2006 11:25:51 -0000
@@ -37,6 +37,7 @@
 #include "rhythmdb-property-model.h"
 #include "rb-stock-icons.h"
 #include "eel-gconf-extensions.h"
+#include "rb-util.h"
 
 static void rb_property_view_class_init (RBPropertyViewClass *klass);
 static void rb_property_view_init (RBPropertyView *view);
@@ -683,6 +684,7 @@
 		}
 		g_signal_emit (G_OBJECT (view), rb_property_view_signals[PROPERTIES_SELECTED], 0,
 			       selected_properties);
+		rb_list_deep_free (selected_properties);
 	} else {
 		if (gtk_tree_selection_get_selected (view->priv->selection, &model, &iter)) {
 			gtk_tree_model_get (model, &iter,
--- /dev/null	2006-02-07 12:09:11.963290464 +1100
+++ widgets/rb-library-browser.c	2006-02-07 20:35:54.000000000 +1100
@@ -0,0 +1,491 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  arch-tag: Implementation of library browser widget
+ *
+ *  Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
+ *  Copyright (C) 2003,2004 Colin Walters <walters@verbum.org>
+ *  Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "rb-library-browser.h"
+#include "rb-preferences.h"
+#include "eel-gconf-extensions.h"
+#include "rhythmdb-property-model.h"
+#include "rhythmdb-query-model.h"
+#include "rb-property-view.h"
+#include "rb-debug.h"
+#include "rb-util.h"
+
+static void rb_library_browser_class_init (RBLibraryBrowserClass *klass);
+static void rb_library_browser_init (RBLibraryBrowser *entry);
+static void rb_library_browser_finalize (GObject *object);
+static void rb_library_browser_set_property (GObject *object,
+					     guint prop_id,
+					     const GValue *value,
+					     GParamSpec *pspec);
+static void rb_library_browser_get_property (GObject *object,
+					     guint prop_id,
+					     GValue *value,
+					     GParamSpec *pspec);
+
+static void rb_library_browser_notify_changed (RBLibraryBrowser *widget);
+static void view_property_selected_cb (RBPropertyView *view,
+				       GList *selection,
+				       RBLibraryBrowser *widget);
+static void view_selection_reset_cb (RBPropertyView *view,
+				     RBLibraryBrowser *widget);
+
+static void update_browser_views_visibility (RBLibraryBrowser *widget);
+static void rb_library_browser_views_changed (GConfClient *client,
+					      guint cnxn_id,
+					      GConfEntry *entry,
+					      RBLibraryBrowser *widget);
+
+
+
+G_DEFINE_TYPE (RBLibraryBrowser, rb_library_browser, GTK_TYPE_HBOX)
+#define RB_LIBRARY_BROWSER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_LIBRARY_BROWSER, RBLibraryBrowserPrivate))
+
+typedef struct
+{
+	RhythmDB *db;
+	RhythmDBQueryModel *model;
+
+	guint browser_view_notify_id;
+	GSList *browser_views_group;
+
+	GHashTable *property_views;
+	GHashTable *selections;
+} RBLibraryBrowserPrivate;
+
+
+enum
+{
+	CHANGED,
+	LAST_SIGNAL
+};
+
+enum
+{
+	PROP_0,
+	PROP_DB
+};
+
+static guint rb_library_browser_signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+	RhythmDBPropType type;
+	const char *name;
+} BrowserPropertyInfo;
+
+static BrowserPropertyInfo browser_properties[] = {
+	{RHYTHMDB_PROP_GENRE, N_("Genre")},
+	{RHYTHMDB_PROP_ARTIST, N_("Artist")},
+	{RHYTHMDB_PROP_ALBUM, N_("Album")}
+};
+const int num_browser_properties = G_N_ELEMENTS (browser_properties);
+
+static void
+rb_library_browser_class_init (RBLibraryBrowserClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = rb_library_browser_finalize;
+	object_class->set_property = rb_library_browser_set_property;
+	object_class->get_property = rb_library_browser_get_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_DB,
+					 g_param_spec_object ("db",
+							      "db",
+							      "RhythmDB instance",
+							      RHYTHMDB_TYPE,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	rb_library_browser_signals[CHANGED] =
+		g_signal_new ("changed",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBLibraryBrowserClass, changed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+
+	g_type_class_add_private (klass, sizeof (RBLibraryBrowserPrivate));
+}
+
+static void
+rb_library_browser_init (RBLibraryBrowser *widget)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+	int i;
+
+	gtk_box_set_spacing (GTK_BOX (widget), 5);
+
+	priv->property_views = g_hash_table_new (g_direct_hash, g_direct_equal);
+	priv->selections = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rb_list_deep_free);
+
+	for (i = 0; i < num_browser_properties; i++) {
+		RBPropertyView *view;
+
+		view = rb_property_view_new (priv->db,
+					     browser_properties[i].type,
+					     browser_properties[i].name);
+		g_hash_table_insert (priv->property_views, (gpointer)(browser_properties[i].type), view);
+	
+		rb_property_view_set_selection_mode (view, GTK_SELECTION_MULTIPLE);
+		g_signal_connect_object (G_OBJECT (view),
+					 "properties-selected",
+					 G_CALLBACK (view_property_selected_cb),
+					 widget, 0);
+		g_signal_connect_object (G_OBJECT (view),
+					 "property-selection-reset",
+					 G_CALLBACK (view_selection_reset_cb),
+					 widget, 0);
+		gtk_widget_show_all (GTK_WIDGET (view));
+		gtk_widget_set_no_show_all (GTK_WIDGET (view), TRUE);
+		gtk_box_pack_start_defaults (GTK_BOX (widget), GTK_WIDGET (view));	     
+	}
+
+	update_browser_views_visibility (widget);
+	priv->browser_view_notify_id =
+		eel_gconf_notification_add (CONF_UI_BROWSER_VIEWS,
+				    (GConfClientNotifyFunc) rb_library_browser_views_changed, widget);
+}
+
+static void
+rb_library_browser_finalize (GObject *object)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (object);
+
+	eel_gconf_notification_remove (priv->browser_view_notify_id);
+
+	g_object_unref (G_OBJECT (priv->db));
+
+	g_hash_table_destroy (priv->property_views);
+	g_hash_table_destroy (priv->selections);
+
+	G_OBJECT_CLASS (rb_library_browser_parent_class)->finalize (object);
+}
+
+static void
+rb_library_browser_set_property (GObject *object,
+				 guint prop_id,
+				 const GValue *value,
+				 GParamSpec *pspec)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (object);
+
+	switch (prop_id)
+	{
+	case PROP_DB:
+		if (priv->db) {
+			g_object_unref (G_OBJECT (priv->db));
+		}
+		priv->db = g_value_get_object (value);
+
+		if (priv->db)
+			g_object_ref (priv->db);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+rb_library_browser_get_property (GObject *object,
+				 guint prop_id,
+				 GValue *value,
+				 GParamSpec *pspec)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (object);
+
+	switch (prop_id)
+	{
+	case PROP_DB:
+		g_value_set_object (value, priv->db);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+RBLibraryBrowser *
+rb_library_browser_new (RhythmDB *db)
+{
+	RBLibraryBrowser *widget;
+
+	widget = RB_LIBRARY_BROWSER (g_object_new (RB_TYPE_LIBRARY_BROWSER,
+						   "db", db,
+						   NULL));
+
+	return widget;
+}
+
+static void
+update_browser_property_visibilty (RhythmDBPropType prop, RBPropertyView *view, GList *properties)
+{
+	gboolean old_vis, new_vis;
+	
+	old_vis = GTK_WIDGET_VISIBLE (view);
+	new_vis = (g_list_find (properties, (gpointer)prop) != NULL);
+
+	if (old_vis != new_vis) {
+		if (new_vis) {
+			gtk_widget_show (GTK_WIDGET (view));
+		} else {
+			gtk_widget_hide (GTK_WIDGET (view));
+			rb_property_view_reset (view);
+		}
+	}
+}
+
+static void
+update_browser_views_visibility (RBLibraryBrowser *widget)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+	GList *properties = NULL;
+	/*char *views_str;
+
+	views_str = eel_gconf_get_string (CONF_UI_BROWSER_VIEWS);
+	if (views_str) {
+		properties = rhythmdb_string_to_props (views_str);
+	} else*/ {
+		int views = eel_gconf_get_integer (CONF_UI_BROWSER_VIEWS);
+
+		if (views == 0 || views == 2)
+			properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_ALBUM);
+		properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_ARTIST);
+		if (views == 1 || views == 2)
+			properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_GENRE);
+	}
+
+	g_hash_table_foreach (priv->property_views, (GHFunc)update_browser_property_visibilty, properties);
+}
+
+static void
+rb_library_browser_views_changed (GConfClient *client,
+			          guint cnxn_id,
+			          GConfEntry *entry,
+			          RBLibraryBrowser *widget)
+{
+	update_browser_views_visibility (widget);
+}
+
+static void
+view_property_selected_cb (RBPropertyView *view,
+			   GList *selection,
+			   RBLibraryBrowser *widget)
+{
+	RhythmDBPropType prop;
+
+	g_object_get (G_OBJECT (view), "prop", &prop, NULL);
+	rb_library_browser_set_selection (widget, prop, selection);
+}
+static void
+view_selection_reset_cb (RBPropertyView *view, RBLibraryBrowser *widget)
+{
+	RhythmDBPropType prop;
+
+	g_object_get (G_OBJECT (view), "prop", &prop, NULL);
+	rb_library_browser_set_selection (widget, prop, NULL);
+}
+
+static void
+rb_library_browser_notify_changed (RBLibraryBrowser *widget)
+{
+	g_signal_emit (G_OBJECT (widget), rb_library_browser_signals[CHANGED], 0);
+}
+
+static void
+reset_view_cb (RhythmDBPropType prop, RBPropertyView *view, RBLibraryBrowser *widget)
+{
+	view_selection_reset_cb (view, widget);
+}
+
+gboolean
+rb_library_browser_reset (RBLibraryBrowser *widget)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+
+	if (rb_library_browser_has_selection (widget)) {
+		g_hash_table_foreach (priv->property_views, (GHFunc)reset_view_cb, widget);
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+typedef struct {
+	RBLibraryBrowser *widget;
+	RhythmDB *db;
+	GPtrArray *query;
+} ConstructQueryData;
+
+static void
+construct_query_cb (RhythmDBPropType type, GList *selections, ConstructQueryData *data)
+{
+	rhythmdb_query_append_prop_multiple (data->db,
+					     data->query,
+					     type,
+					     selections);
+}
+
+GPtrArray*
+rb_library_browser_construct_query (RBLibraryBrowser *widget)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+	GPtrArray *query;
+	ConstructQueryData *data;
+	
+	query = g_ptr_array_new ();
+	data = g_new0 (ConstructQueryData, 1);
+	data->widget = widget;
+	data->db = priv->db;
+	data->query = query;
+
+	g_hash_table_foreach (priv->selections, (GHFunc)construct_query_cb, data);
+	g_free (data);
+
+	return query;
+}
+
+gboolean
+rb_library_browser_has_selection (RBLibraryBrowser *widget)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+
+	return (g_hash_table_size (priv->selections) > 0);
+}
+
+static gint
+prop_to_index (RhythmDBPropType type)
+{
+	int i;
+
+	for (i = 0; i < num_browser_properties; i++)
+		if (browser_properties[i].type == type)
+			return i;
+
+	return -1;
+}
+
+static void
+rebuild_child_model (RBLibraryBrowser *widget, gint property_index)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+	RhythmDBPropertyModel *prop_model;
+	RhythmDBQueryModel *base_model, *child_model;
+	RBPropertyView *view;
+	GPtrArray *query;
+	GList *selections;
+
+	g_assert (property_index >= 0);
+	g_assert (property_index < num_browser_properties);
+
+	/* there is no model after the last one to update*/
+	if (property_index == num_browser_properties - 1)
+		return;
+			
+	/* get the query model for the previous property view */
+	view = g_hash_table_lookup (priv->property_views, (gpointer)browser_properties[property_index].type);
+	prop_model = rb_property_view_get_model (view);
+	g_object_get (G_OBJECT (prop_model), "query-model", &base_model, NULL);
+
+	/* create a new query model based on it, filtered by the selections of the previous property view */
+	selections = g_hash_table_lookup (priv->selections, (gpointer)browser_properties[property_index].type);
+	query = g_ptr_array_new ();
+	rhythmdb_query_append_prop_multiple (priv->db,
+					     query,
+					     browser_properties[property_index].type,
+					     selections);
+
+	child_model = rhythmdb_query_model_new_empty (priv->db);
+	g_object_set (G_OBJECT (child_model), "base-model", base_model, NULL);
+	g_object_unref (G_OBJECT (base_model));
+
+	g_object_set (G_OBJECT (child_model), "query", query, NULL);
+	rhythmdb_query_model_reapply_query (child_model, TRUE);
+	rhythmdb_query_free (query);
+	
+	/* apply it as the query model of the current property view */
+	view = g_hash_table_lookup (priv->property_views, (gpointer)browser_properties[property_index+1].type);
+	prop_model = rb_property_view_get_model (view);
+	g_object_set (G_OBJECT (prop_model), "query-model", child_model, NULL);
+	g_object_unref (G_OBJECT (child_model));
+
+	rebuild_child_model (widget, property_index + 1);
+}
+
+void
+rb_library_browser_set_selection (RBLibraryBrowser *widget, RhythmDBPropType type, GList *selection)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+	GList *old_selection;
+	
+	old_selection = g_hash_table_lookup (priv->selections, (gpointer)type);
+
+	if (rb_string_list_equal (old_selection, selection))
+		return;
+
+	if (selection)
+		g_hash_table_insert (priv->selections, (gpointer)type, rb_string_list_copy (selection));
+	else
+		g_hash_table_remove (priv->selections, (gpointer)type);
+
+	rebuild_child_model (widget, prop_to_index (type));
+	rb_library_browser_notify_changed (widget);
+}
+
+GList*
+rb_library_browser_get_property_views (RBLibraryBrowser *widget)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+
+	return rb_collate_hash_table_values (priv->property_views);
+}
+
+void
+rb_library_browser_set_model (RBLibraryBrowser *widget, RhythmDBQueryModel *model)
+{
+	RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget);
+	RBPropertyView *view;
+	RhythmDBPropertyModel *prop_model;
+	
+	if (priv->model)
+		g_object_unref (G_OBJECT (priv->model));
+
+	priv->model = model;
+	g_object_ref (model);
+
+	view = g_hash_table_lookup (priv->property_views, (gpointer)browser_properties[0].type);
+	prop_model = rb_property_view_get_model (view);
+	g_object_set (G_OBJECT (prop_model), "query-model", priv->model, NULL);
+
+	rebuild_child_model (widget, 0);
+}
--- /dev/null	2006-02-07 12:09:11.963290464 +1100
+++ widgets/rb-library-browser.h	2006-02-07 19:39:31.000000000 +1100
@@ -0,0 +1,66 @@
+/*
+ *  arch-tag: Header for library browser widget
+ *
+ *  Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_LIBRARY_BROWSER_H
+#define __RB_LIBRARY_BROWSER_H
+
+#include <gtk/gtkhbox.h>
+
+#include "rhythmdb.h"
+#include "rhythmdb-query-model.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_LIBRARY_BROWSER         (rb_library_browser_get_type ())
+#define RB_LIBRARY_BROWSER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_LIBRARY_BROWSER, RBLibraryBrowser))
+#define RB_LIBRARY_BROWSER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_LIBRARY_BROWSER, RBLibraryBrowserClass))
+#define RB_IS_LIBRARY_BROWSER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_LIBRARY_BROWSER))
+#define RB_IS_LIBRARY_BROWSER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_LIBRARY_BROWSER))
+#define RB_LIBRARY_BROWSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_LIBRARY_BROWSER, RBLibraryBrowserClass))
+
+typedef struct
+{
+	GtkHBox parent;
+} RBLibraryBrowser;
+
+typedef struct
+{
+	GtkHBoxClass parent;
+
+	void (*changed) (RBLibraryBrowser *entry);
+} RBLibraryBrowserClass;
+
+GType		rb_library_browser_get_type (void);
+RBLibraryBrowser *	rb_library_browser_new      (RhythmDB *db);
+
+void rb_library_browser_set_model (RBLibraryBrowser *widget, RhythmDBQueryModel *model);
+
+gboolean rb_library_browser_reset (RBLibraryBrowser *widget);
+GPtrArray* rb_library_browser_construct_query (RBLibraryBrowser *widget);
+GList* rb_library_browser_get_property_views (RBLibraryBrowser *widget);
+
+gboolean rb_library_browser_has_selection (RBLibraryBrowser *widget);
+void rb_library_browser_set_selection (RBLibraryBrowser *widget, RhythmDBPropType type, GList *list);
+
+
+G_END_DECLS
+
+#endif /* __RB_LIBRARY_BROWSER_H */
