Index: rhythmdb/rhythmdb-tree.c
===================================================================
--- rhythmdb/rhythmdb-tree.c	(revision 6291)
+++ rhythmdb/rhythmdb-tree.c	(working copy)
@@ -1085,18 +1085,25 @@
 
 			for (l = keywords; l != NULL; l = g_list_next (l)) {
 				RBRefString *keyword = (RBRefString*)l->data;
+				const char *keyword_str = rb_refstring_get (keyword);
 
-				RHYTHMDB_FWRITE_STATICSTR ("    <keyword>", ctx->handle, ctx->error);
-				encoded	= xmlEncodeEntitiesReentrant (NULL, BAD_CAST rb_refstring_get (keyword));
-				RHYTHMDB_FWRITE (encoded, 1, xmlStrlen (encoded), ctx->handle, ctx->error);
-				g_free (encoded);
-				RHYTHMDB_FWRITE_STATICSTR ("</keyword>\n", ctx->handle, ctx->error);
+				if (keyword_str[0] != '!') {
+					RHYTHMDB_FWRITE_STATICSTR ("    <keyword>", ctx->handle, ctx->error);
+					encoded	= xmlEncodeEntitiesReentrant (NULL, BAD_CAST rb_refstring_get (keyword));
+					RHYTHMDB_FWRITE (encoded, 1, xmlStrlen (encoded), ctx->handle, ctx->error);
+					g_free (encoded);
+					RHYTHMDB_FWRITE_STATICSTR ("</keyword>\n", ctx->handle, ctx->error);
+				} else {
+					rb_debug ("Not saving keyword %s", keyword_str);
+					//printf ("Not saving keyword %s\n", keyword_str);
+				}
 
 				rb_refstring_unref (keyword);
 			}
 
 			g_list_free (keywords);
 			break;
+		case RHYTHMDB_PROP_PLAYLIST:
 		case RHYTHMDB_PROP_TITLE_SORT_KEY:
 		case RHYTHMDB_PROP_GENRE_SORT_KEY:
 		case RHYTHMDB_PROP_ARTIST_SORT_KEY:
Index: rhythmdb/rhythmdb.c
===================================================================
--- rhythmdb/rhythmdb.c	(revision 6291)
+++ rhythmdb/rhythmdb.c	(working copy)
@@ -4216,6 +4216,7 @@
 			ENUM_ENTRY (RHYTHMDB_PROP_HIDDEN, "Hidden (gboolean) [hidden]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_FIRST_SEEN_STR, "Time Added to Library (gchararray) [first-seen-str]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_LAST_SEEN_STR, "Last time the song was available (gchararray) [last-seen-str]"),
+			ENUM_ENTRY (RHYTHMDB_PROP_PLAYLIST, "Playlist Membership (gulong) [playlist]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_SEARCH_MATCH, "Search matching key (gchararray) [search-match]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_YEAR, "Year of date (gulong) [year]"),
 
Index: rhythmdb/rhythmdb.h
===================================================================
--- rhythmdb/rhythmdb.h	(revision 6291)
+++ rhythmdb/rhythmdb.h	(working copy)
@@ -150,7 +150,7 @@
 	RHYTHMDB_QUERY_PROP_YEAR_LESS,
 } RhythmDBQueryType;
 
-/* If you modify this enum, don't forget to modify rhythmdb_prop_get_type */
+/* If you modify this enum, don't forget to modify rhythmdb_prop_type_get_type */
 typedef enum
 {
 	RHYTHMDB_PROP_TYPE = 0,
@@ -191,6 +191,7 @@
 	RHYTHMDB_PROP_PLAYBACK_ERROR,
 	RHYTHMDB_PROP_FIRST_SEEN_STR,
 	RHYTHMDB_PROP_LAST_SEEN_STR,
+	RHYTHMDB_PROP_PLAYLIST,
 
 	/* synthetic properties */
 	RHYTHMDB_PROP_SEARCH_MATCH,
Index: sources/rb-playlist-source.c
===================================================================
--- sources/rb-playlist-source.c	(revision 6291)
+++ sources/rb-playlist-source.c	(working copy)
@@ -131,7 +131,6 @@
 	RhythmDB *db;
 
 	GHashTable *entries;
-
 	RhythmDBQueryModel *model;
 
 	RBEntryView *songs;
@@ -142,6 +141,7 @@
 
 	char *title;
 	char *sorting_name;
+	RBRefString *present_keyword;
 };
 
 #define RB_PLAYLIST_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PLAYLIST_SOURCE, RBPlaylistSourcePrivate))
@@ -157,6 +157,8 @@
 
 static const GtkTargetEntry target_uri [] = { { "text/uri-list", 0, 0 } };
 
+static gint current_playlist_id = 0;
+
 G_DEFINE_ABSTRACT_TYPE (RBPlaylistSource, rb_playlist_source, RB_TYPE_SOURCE);
 
 static void
@@ -245,7 +247,20 @@
 static void
 rb_playlist_source_init (RBPlaylistSource *source)
 {
+	char *keyword;
+	gint id;
+	
 	source->priv = RB_PLAYLIST_SOURCE_GET_PRIVATE (source);
+	
+	source->priv->entries = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
+						       (GDestroyNotify)rb_refstring_unref, NULL);
+
+	id = current_playlist_id++;
+	keyword = g_strdup_printf ("!rb:playlist:present:%d", id);
+	rb_debug ("playlist %p has present-keyword of %s", source, keyword);
+	//printf ("playlist has present-keyword of %s\n", keyword);
+	source->priv->present_keyword = rb_refstring_new (keyword);
+	g_free (keyword);
 }
 
 static void
@@ -411,6 +426,7 @@
 	rb_debug ("Finalizing playlist source %p", source);
 
 	g_hash_table_destroy (source->priv->entries);
+	rb_refstring_unref (source->priv->present_keyword);
 
 	g_free (source->priv->title);
 	source->priv = NULL;
@@ -808,12 +824,34 @@
 	location = rhythmdb_entry_get_refstring (entry, RHYTHMDB_PROP_LOCATION);
 	if (g_hash_table_remove (source->priv->entries, location))
 		source->priv->dirty = TRUE;
-
 	rb_refstring_unref (location);
+	
+	rhythmdb_entry_keyword_remove (source->priv->db, entry, source->priv->present_keyword);
+	rb_debug ("Removing keyword %s", rb_refstring_get (source->priv->present_keyword));
+	//printf ("Removing keyword %s\n", rb_refstring_get (source->priv->present_keyword));
+	
 	rhythmdb_entry_unref (entry);
 }
 
 static void
+rb_playlist_source_row_inserted (GtkTreeModel *model,
+				 GtkTreePath *path,
+				 GtkTreeIter *iter,
+				 RBPlaylistSource *source)
+{
+	RhythmDBEntry *entry;
+	
+	entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (model),
+							 path);
+	
+	rhythmdb_entry_keyword_add (source->priv->db, entry, source->priv->present_keyword);
+	rb_debug ("Adding Keyword %s", rb_refstring_get (source->priv->present_keyword));
+	//printf ("Adding Keyword %s\n", rb_refstring_get (source->priv->present_keyword));
+	
+	rhythmdb_entry_unref (entry);
+}
+
+static void
 rb_playlist_source_entry_added_cb (RhythmDB *db,
 				   RhythmDBEntry *entry,
 				   RBPlaylistSource *source)
@@ -907,6 +945,9 @@
 		g_signal_handlers_disconnect_by_func (source->priv->model,
 						      G_CALLBACK (rb_playlist_source_row_deleted),
 						      source);
+		g_signal_handlers_disconnect_by_func (source->priv->model,
+						      G_CALLBACK (rb_playlist_source_row_inserted),
+						      source);
 		g_object_unref (source->priv->model);
 	}
 
@@ -916,6 +957,8 @@
 		g_object_ref (source->priv->model);
 		g_signal_connect_object (source->priv->model, "row_deleted",
 					 G_CALLBACK (rb_playlist_source_row_deleted), source, 0);
+		g_signal_connect_object (source->priv->model, "row_inserted",
+					 G_CALLBACK (rb_playlist_source_row_inserted), source, 0);
 	}
 
 	rb_entry_view_set_model (source->priv->songs, RHYTHMDB_QUERY_MODEL (source->priv->model));
Index: widgets/rb-query-creator-properties.c
===================================================================
--- widgets/rb-query-creator-properties.c	(revision 6291)
+++ widgets/rb-query-creator-properties.c	(working copy)
@@ -45,6 +45,7 @@
 const RBQueryCreatorPropertyType year_property_type;
 const RBQueryCreatorPropertyType duration_property_type;
 const RBQueryCreatorPropertyType relative_time_property_type;
+const RBQueryCreatorPropertyType playlist_property_type;
 
 static GtkWidget * stringCriteriaCreateWidget (gboolean *constrain);
 static void stringCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
@@ -66,6 +67,9 @@
 static GtkWidget * relativeTimeCriteriaCreateWidget (gboolean *constrain);
 static void relativeTimeCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
 static void relativeTimeCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static GtkWidget * playlistCriteriaCreateWidget (gboolean *constrain);
+static void playlistCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void playlistCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
 
 /*
  * This table is the list of properties that are displayed in the query-creator
@@ -89,6 +93,8 @@
 
 	{ N_("Time of Last Play"), RHYTHMDB_PROP_LAST_PLAYED, RHYTHMDB_PROP_LAST_PLAYED, &relative_time_property_type },
 	{ N_("Time Added to Library"), RHYTHMDB_PROP_FIRST_SEEN, RHYTHMDB_PROP_FIRST_SEEN, &relative_time_property_type },
+	
+	{ N_("Playlist"), RHYTHMDB_PROP_PLAYLIST, RHYTHMDB_PROP_PLAYLIST, &playlist_property_type },
 };
 
 const int num_property_options = G_N_ELEMENTS (property_options);
@@ -148,6 +154,33 @@
 };
 
 /*
+ * This is the property type for playlist properties, such as is or isn't in a playlist
+ */
+
+typedef struct
+{
+	const char *name;
+	gint playlistID;
+} RBQueryCreatorPlaylistOption;
+
+const RBQueryCreatorCriteriaOption playlist_criteria_options[] =
+{
+	{ N_("is in"), 1, RHYTHMDB_QUERY_PROP_EQUALS },
+	{ N_("isn't in"), 0, RHYTHMDB_QUERY_PROP_NOT_LIKE },
+};
+
+const RBQueryCreatorPropertyType playlist_property_type =
+{
+	G_N_ELEMENTS (playlist_criteria_options),
+	playlist_criteria_options,
+	playlistCriteriaCreateWidget,
+	playlistCriteriaSetWidgetData,
+	playlistCriteriaGetWidgetData
+};
+
+RBQueryCreatorPlaylistOption playlist_options[4];
+
+/*
  * This are the property types for numeric quantities, such as rating and playcounts
  */
 
@@ -558,3 +591,54 @@
 	g_value_init (val, G_TYPE_ULONG);
 	g_value_set_ulong (val, (gulong) value);
 }
+
+/*
+ * Implementation for the playlist properties, using a single menu
+ */
+
+static void
+get_playlist_options (RBQueryCreatorPlaylistOption *options,
+		      int length)
+{
+	options[0].name = N_("Any");
+	options[0].playlistID = -1;
+	int i;
+	
+	for (i=1; i < length; i++) {
+		options[i].name = N_("Playlist Here");
+		options[i].playlistID = i;
+	}
+}
+
+static GtkWidget *
+playlistCriteriaCreateWidget (gboolean *constrain)
+{
+	/* Fetch the list of playlists */
+	get_playlist_options (playlist_options, G_N_ELEMENTS (playlist_options));
+
+	/* Create the widget */
+	GtkWidget *combo;
+	int i;
+	int length = G_N_ELEMENTS (playlist_options);
+
+	combo = gtk_combo_box_new_text ();
+	for (i = 0; i < length; i++) {
+		gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _(playlist_options[i].name));
+	}
+	
+	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+
+	return combo;
+}
+
+static void
+playlistCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	
+}
+
+static void
+playlistCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	
+}
