diff -b -u -r1.8 create-playlist.glade
--- data/glade/create-playlist.glade	25 Jul 2005 16:30:55 -0000	1.8
+++ data/glade/create-playlist.glade	23 Aug 2005 14:49:44 -0000
@@ -261,8 +277,84 @@
 		    </widget>
 		    <packing>
 		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
 		      <property name="expand">True</property>
 		      <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHBox" id="hbox201">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">12</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="label1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">When sorted by</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkOptionMenu" id="sortMenu">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="history">-1</property>
+
+		      <child>
+			<widget class="GtkMenu" id="menu9">
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkCheckButton" id="sortDesc">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">False</property>
+		      <property name="inconsistent">False</property>
+		      <property name="draw_indicator">True</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		      <property name="pack_type">GTK_PACK_END</property>
 		    </packing>
 		  </child>
 		</widget>
diff -b -u -r1.66 rhythmdb-tree.c
--- rhythmdb/rhythmdb-tree.c	10 Jul 2005 12:37:56 -0000	1.66
+++ rhythmdb/rhythmdb-tree.c	23 Aug 2005 14:50:38 -0000
@@ -1214,6 +1214,9 @@
 			GList *conjunctions = split_query_by_disjunctions (dbtree, data->subquery);
 			GList *tem;
 
+			if (conjunctions == NULL)
+				matched = TRUE;
+
 			for (tem = conjunctions; tem; tem = tem->next) {
 				GPtrArray *subquery = tem->data;
 				if (!matched && evaluate_conjunctive_subquery (dbtree, subquery,
@@ -1228,6 +1231,26 @@
 				return FALSE;
 		}
 		break;
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
+		{
+			gulong relative_time;
+			GTimeVal current_time;
+
+			g_assert (rhythmdb_get_property_type (db, data->propid) == G_TYPE_ULONG);
+
+			relative_time = g_value_get_ulong (data->val);
+			g_get_current_time  (&current_time);
+
+			rb_debug ("relative time query: cur=%u, rel=%u", current_time.tv_sec, relative_time);
+
+			if (data->type == RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN)
+				return (rhythmdb_entry_get_ulong (entry, data->propid) >= (current_time.tv_sec - relative_time));
+			else
+				return (rhythmdb_entry_get_ulong (entry, data->propid) < (current_time.tv_sec - relative_time));
+
+			break;
+		}
 		case RHYTHMDB_QUERY_PROP_LIKE:
 		case RHYTHMDB_QUERY_PROP_NOT_LIKE:
 		{
diff -b -u -r1.108 rhythmdb.c
--- rhythmdb/rhythmdb.c	17 Aug 2005 09:37:42 -0000	1.108
+++ rhythmdb/rhythmdb.c	23 Aug 2005 14:51:00 -0000
@@ -58,6 +58,8 @@
 #define RB_PARSE_DISJ (xmlChar *) "disjunction"
 #define RB_PARSE_GREATER (xmlChar *) "greater"
 #define RB_PARSE_LESS (xmlChar *) "less"
+#define RB_PARSE_CURRENT_TIME_WITHIN (xmlChar *) "current-time-within"
+#define RB_PARSE_CURRENT_TIME_NOT_WITHIN (xmlChar *) "current-time-not-within"
 
 #define RB_PARSE_NICK_START (xmlChar *) "["
 #define RB_PARSE_NICK_END (xmlChar *) "]"
@@ -2171,6 +2173,8 @@
 		case RHYTHMDB_QUERY_PROP_NOT_LIKE:
 		case RHYTHMDB_QUERY_PROP_GREATER:
 		case RHYTHMDB_QUERY_PROP_LESS:
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
 			data->propid = va_arg (args, guint);
 			data->val = g_new0 (GValue, 1);
 			g_value_init (data->val, rhythmdb_get_property_type (db, data->propid));
@@ -2241,6 +2245,8 @@
 		case RHYTHMDB_QUERY_PROP_NOT_LIKE:
 		case RHYTHMDB_QUERY_PROP_GREATER:
 		case RHYTHMDB_QUERY_PROP_LESS:
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
 			g_value_unset (data->val);
 			g_free (data->val);
 			break;
@@ -2399,6 +2405,16 @@
 			xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid));
 			write_encoded_gvalue (subnode, data->val);
 			break;
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
+			subnode = xmlNewChild (node, NULL, RB_PARSE_CURRENT_TIME_WITHIN, NULL);
+			xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid));
+			write_encoded_gvalue (subnode, data->val);
+			break;
+		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
+			subnode = xmlNewChild (node, NULL, RB_PARSE_CURRENT_TIME_NOT_WITHIN, NULL);
+			xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid));
+			write_encoded_gvalue (subnode, data->val);
+			break;
 		}		
 	}
 }
@@ -2439,6 +2455,10 @@
 			data->type = RHYTHMDB_QUERY_PROP_GREATER;
 		} else if (!xmlStrcmp (child->name, RB_PARSE_LESS)) {
 			data->type = RHYTHMDB_QUERY_PROP_LESS;
+		} else if (!xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_WITHIN)) {
+			data->type = RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN;
+		} else if (!xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_NOT_WITHIN)) {
+			data->type = RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN;
 		} else
  			g_assert_not_reached ();
 
@@ -2446,7 +2466,9 @@
 		    || !xmlStrcmp (child->name, RB_PARSE_NOT_LIKE)
 		    || !xmlStrcmp (child->name, RB_PARSE_EQUALS)
 		    || !xmlStrcmp (child->name, RB_PARSE_GREATER)
-		    || !xmlStrcmp (child->name, RB_PARSE_LESS)) {
+		    || !xmlStrcmp (child->name, RB_PARSE_LESS)
+		    || !xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_WITHIN)
+		    || !xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_NOT_WITHIN)) {
 			xmlChar *propstr = xmlGetProp (child, RB_PARSE_PROP);
 			gint propid = rhythmdb_propid_from_nice_elt_name (db, propstr);
 			g_free (propstr);
@@ -2635,6 +2657,8 @@
 			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_NOT_LIKE, "Inverted fuzzy property matching"),
 			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_GREATER, "True if property1 >= property2"),
 			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_LESS, "True if property1 <= property2"),
+			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN, "True if property1 is within property2 of the current time"),
+			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN, "True if property1 is not within property2 of the current time"),
 			{ 0, 0, 0 }
 		};
 
diff -b -u -r1.43 rhythmdb.h
--- rhythmdb/rhythmdb.h	30 Jul 2005 05:42:57 -0000	1.43
+++ rhythmdb/rhythmdb.h	23 Aug 2005 14:51:03 -0000
@@ -59,6 +59,8 @@
 	RHYTHMDB_QUERY_PROP_NOT_LIKE,
 	RHYTHMDB_QUERY_PROP_GREATER,
 	RHYTHMDB_QUERY_PROP_LESS,
+	RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN,
+	RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN,
 } RhythmDBQueryType;
 
 /* If you modify this enum, don't forget to modify rhythmdb_prop_get_type */
diff -b -u -r1.49 rb-playlist-manager.c
--- shell/rb-playlist-manager.c	15 Aug 2005 13:18:02 -0000	1.49
+++ shell/rb-playlist-manager.c	23 Aug 2005 14:51:09 -0000
@@ -737,15 +737,21 @@
 {
 	RBQueryCreatorLimitType type;
 	guint limit, limit_count = 0, limit_size = 0;
+	const char *sort_key;
+	gint sort_direction;
 
 	rb_query_creator_get_limit (creator, &type, &limit);
 	if (type == RB_QUERY_CREATOR_LIMIT_COUNT)
 		limit_count = limit;
 	else if (type == RB_QUERY_CREATOR_LIMIT_MB)
 		limit_size = limit;
+
+	rb_query_creator_get_sort_order (creator, &sort_key, &sort_direction);
+
 	rb_playlist_source_set_query (playlist,
 				      rb_query_creator_get_query (creator),
-				      limit_count, limit_size);
+				      limit_count, limit_size,
+				      sort_key, sort_direction);
 }
 
 static void
@@ -780,15 +786,17 @@
 	RBPlaylistSource *playlist;
 	GPtrArray *query;
 	guint limit_count = 0, limit_size = 0;
+	const char *sort_key;
+	gint sort_direction;
 	
 
 	playlist = RB_PLAYLIST_SOURCE (mgr->priv->selected_source);
-	rb_playlist_source_get_query (playlist, &query, &limit_count, &limit_size);
+	rb_playlist_source_get_query (playlist, &query, &limit_count, &limit_size, &sort_key, &sort_direction);
 
 	creator = RB_QUERY_CREATOR (rb_query_creator_new_from_query (mgr->priv->db,
 								     query,
-								     limit_count,
-								     limit_size));	
+								     limit_count, limit_size,
+								     sort_key, sort_direction));
 
 	gtk_dialog_run (GTK_DIALOG (creator));
 
diff -b -u -r1.65 rb-playlist-source.c
--- sources/rb-playlist-source.c	17 Aug 2005 16:32:54 -0000	1.65
+++ sources/rb-playlist-source.c	23 Aug 2005 14:51:41 -0000
@@ -55,6 +55,8 @@
 #define RB_PLAYLIST_INT_NAME (xmlChar *) "internal-name"
 #define RB_PLAYLIST_LIMIT_COUNT (xmlChar *) "limit-count"
 #define RB_PLAYLIST_LIMIT_SIZE (xmlChar *) "limit-size"
+#define RB_PLAYLIST_SORT_KEY (xmlChar *) "sort-key"
+#define RB_PLAYLIST_SORT_DIRECTION (xmlChar *) "sort-direction"
 #define RB_PLAYLIST_LIMIT (xmlChar *) "limit"
 #define RB_PLAYLIST_LOCATION (xmlChar *) "location"
 
@@ -87,6 +89,8 @@
 static gboolean impl_show_popup (RBSource *source);
 static void rb_playlist_source_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
 					       RBPlaylistSource *source);
+static void rb_playlist_source_songs_sort_order_changed_cb (RBEntryView *view,
+							    RBPlaylistSource *source);
 
 static void rb_playlist_source_songs_show_popup_cb (RBEntryView *view, RBPlaylistSource *playlist_view);
 static void rb_playlist_source_drop_cb (GtkWidget *widget,
@@ -99,6 +103,10 @@
 				     gpointer user_data);
 static void rb_playlist_source_add_list_uri (RBPlaylistSource *source,
 					  GList *list);
+static void rb_playlist_source_do_query (RBPlaylistSource *source,
+					 GPtrArray *query,
+					 guint limit_count,
+					 guint limit_mb);
 
 #define PLAYLIST_SOURCE_SONGS_POPUP_PATH "/PlaylistViewPopup"
 #define PLAYLIST_SOURCE_POPUP_PATH "/PlaylistSourcePopup"
@@ -114,6 +122,7 @@
 	GHashTable *entries;
 
 	RhythmDBQueryModel *model;
+	gboolean query_resetting;
 
 	GtkWidget *vbox;
 	GdkPixbuf *normal_pixbuf;
@@ -297,10 +306,11 @@
 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING);
 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT);
 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED);
-	rb_entry_view_set_columns_clickable (source->priv->songs, FALSE);
 
 	g_signal_connect_object (G_OBJECT (source->priv->songs), "show_popup",
 				 G_CALLBACK (rb_playlist_source_songs_show_popup_cb), source, 0);
+	g_signal_connect_object (G_OBJECT (source->priv->songs), "sort-order-changed",
+				 G_CALLBACK (rb_playlist_source_songs_sort_order_changed_cb), source, 0);
 		
 	g_signal_connect_object (G_OBJECT (source->priv->songs), "drag_data_received",
 				 G_CALLBACK (rb_playlist_source_drop_cb), source, 0);
@@ -317,6 +327,9 @@
 								   NULL);
 	gtk_widget_destroy (dummy);
 		
+	rb_entry_view_set_columns_clickable (source->priv->songs, FALSE);
+	source->priv->query_resetting = FALSE;
+
 	gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), GTK_WIDGET (source->priv->songs));
 		
 	gtk_widget_show_all (GTK_WIDGET (source));
@@ -444,45 +457,34 @@
 	}
 }
 
-RhythmDBQueryModel *
-rb_playlist_source_get_model (RBPlaylistSource *source)
-{
-	return source->priv->model;
-}
-
 void
 rb_playlist_source_set_query (RBPlaylistSource *source,
 			      GPtrArray *query,
 			      guint limit_count,
-			      guint limit_mb)
+			      guint limit_mb,
+			      const char *sort_key,
+			      gint sort_direction)
 {
-	RhythmDBQueryModel *query_model;
-	GtkTreeModel *model;
-	
 	g_assert (source->priv->automatic);
 
-	source->priv->model = query_model
-		= g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
-				"db", source->priv->db,
-				"max-count", limit_count,
-				"max-size", limit_mb, 
-				NULL);
+	source->priv->query_resetting = TRUE;
 
-	model = GTK_TREE_MODEL (query_model);
-
-	rb_entry_view_set_model (source->priv->songs, RHYTHMDB_QUERY_MODEL (query_model));
-
-	rhythmdb_do_full_query_async_parsed (source->priv->db, model, query);
-
-	g_object_unref (G_OBJECT (query_model));
-	rb_entry_view_poll_model (source->priv->songs);
+	/* playlists that aren't limited, with a particular sort order, are user-orderable */
+	rb_entry_view_set_columns_clickable (source->priv->songs, (limit_count == 0 && limit_mb == 0));
+	rb_entry_view_set_sorting_order (source->priv->songs, sort_key, sort_direction);
+
+	rb_playlist_source_do_query (source, query, limit_count, limit_mb);
+	rhythmdb_query_free (query);
+	source->priv->query_resetting = FALSE;
 }
 
 void
 rb_playlist_source_get_query (RBPlaylistSource *source,
 			      GPtrArray **query,
 			      guint *limit_count,
-			      guint *limit_mb)
+			      guint *limit_mb,
+			      const char **sort_key,
+			      gint *sort_direction)
 {
 	g_assert (source->priv->automatic);
 
@@ -490,6 +492,8 @@
 		      "query", query,
 		      "max-count", limit_count,
 		      "max-size", limit_mb, NULL);
+
+	rb_entry_view_get_sorting_order (source->priv->songs, sort_key, sort_direction);
 }
 
 static char *
@@ -627,7 +631,7 @@
 					      RHYTHMDB_QUERY_SUBQUERY,
 					      subquery,
 					      RHYTHMDB_QUERY_END);
-                rb_playlist_source_set_query (source, query, 0, 0);
+                rb_playlist_source_set_query (source, query, 0, 0, NULL, 0);
 	}
 
         return TRUE;
@@ -866,30 +870,48 @@
 
 	if (source->priv->automatic) {
 		GPtrArray *query;
-		xmlChar *limit_str;
 		gint limit_count = 0;
 		gint limit_mb = 0;
+		gchar *sort_key = NULL;
+		gint sort_direction = 0;
 
 		child = node->children;
 		while (xmlNodeIsText (child))
 			child = child->next;
 
 		query = rhythmdb_query_deserialize (source->priv->db, child);
-		limit_str = xmlGetProp (node, RB_PLAYLIST_LIMIT_COUNT);
-		if (!limit_str) /* Backwards compatibility */
-			limit_str = xmlGetProp (node, RB_PLAYLIST_LIMIT);
-		if (limit_str) {
-			limit_count = atoi ((char *) limit_str);
-			g_free (limit_str);
-		}
-		limit_str = xmlGetProp (node, RB_PLAYLIST_LIMIT_SIZE);
-		if (limit_str) {
-			limit_mb = atoi ((char *) limit_str);
-			g_free (limit_str);
+		tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT_COUNT);
+		if (!tmp) /* Backwards compatibility */
+			tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT);
+		if (tmp) {
+			limit_count = atoi ((char*) tmp);
+			g_free (tmp);
+		}
+		tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT_SIZE);
+		if (tmp) {
+			limit_mb = atoi ((char*) tmp);
+			g_free (tmp);
 		}
+
+		sort_key = (gchar*) xmlGetProp (node, RB_PLAYLIST_SORT_KEY);
+		if (sort_key && *sort_key) {
+			tmp = xmlGetProp (node, RB_PLAYLIST_SORT_DIRECTION);
+			if (tmp) {
+				sort_direction = atoi ((char*) tmp);
+				g_free (tmp);
+			}
+		} else {
+			g_free (sort_key);
+			sort_key = NULL;
+			sort_direction = 0;
+		}
+
 		rb_playlist_source_set_query (source, query,
 					      limit_count,
-					      limit_mb);
+					      limit_mb,
+					      sort_key,
+					      sort_direction);
+		g_free (sort_key);
 	} else {
 		for (child = node->children; child; child = child->next) {
 			xmlChar *location;
@@ -945,20 +967,75 @@
 		GPtrArray *query;
 		guint max_count;
 		guint max_size_mb;
-		char *limit_str;
+		const gchar *sort_key;
+		gint sort_direction;
+		gchar *temp_str;
+
+		rb_playlist_source_get_query (source,
+					      &query,
+					      &max_count, &max_size_mb,
+					      &sort_key, &sort_direction);
+		temp_str = g_strdup_printf ("%d", max_count);
+		xmlSetProp (node, RB_PLAYLIST_LIMIT_COUNT, BAD_CAST temp_str);
+		g_free (temp_str);
+		temp_str = g_strdup_printf ("%d", max_size_mb);
+		xmlSetProp (node, RB_PLAYLIST_LIMIT_SIZE, BAD_CAST temp_str);
+		g_free (temp_str);
+
+		if (sort_key && *sort_key) {
+			xmlSetProp (node, RB_PLAYLIST_SORT_KEY, BAD_CAST sort_key);
+			temp_str = g_strdup_printf ("%d", sort_direction);
+			xmlSetProp (node, RB_PLAYLIST_SORT_DIRECTION, BAD_CAST temp_str);
+			g_free (temp_str);
+		}
 
-		g_object_get (G_OBJECT (source->priv->model),
-			      "max-count", &max_count,
-			      "max-size", &max_size_mb,
-			      "query", &query, NULL);
-		limit_str = g_strdup_printf ("%d", max_count);
-		xmlSetProp (node, RB_PLAYLIST_LIMIT_COUNT, BAD_CAST limit_str);
-		g_free (limit_str);
-		limit_str = g_strdup_printf ("%d", max_size_mb);
-		xmlSetProp (node, RB_PLAYLIST_LIMIT_SIZE, BAD_CAST limit_str);
-		g_free (limit_str);
 		rhythmdb_query_serialize (source->priv->db, query, node);
 	}
 
 	source->priv->dirty = FALSE;
+}
+
+static void
+rb_playlist_source_songs_sort_order_changed_cb (RBEntryView *view, RBPlaylistSource *source)
+{
+	GPtrArray *query;
+	guint limit_count;
+	guint limit_mb;
+	const gchar *sort_key;
+	gint sort_direction;
+
+	g_assert (source->priv->automatic);
+
+	/* don't process this if we are in the middle of setting a query */
+	if (source->priv->query_resetting)
+		return;
+	rb_debug ("sort order changed");
+
+	/* need to re-run query with the same settings*/
+	g_object_get (G_OBJECT (source->priv->model),
+		      "query", &query,
+		      "max-count", &limit_count,
+		      "max-size", &limit_mb,
+		      NULL);
+
+	rb_playlist_source_do_query (source, query, limit_count, limit_mb);
+	rhythmdb_query_free (query);
+}
+
+static void
+rb_playlist_source_do_query (RBPlaylistSource *source,
+			      GPtrArray *query,
+			      guint limit_count,
+			      guint limit_mb)
+{
+	g_assert (source->priv->automatic);
+
+	source->priv->model = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
+				    "db", source->priv->db,
+				    "max-count", limit_count,
+				    "max-size", limit_mb, 
+				    NULL);
+
+	rb_entry_view_set_model (source->priv->songs, source->priv->model);
+	rhythmdb_do_full_query_async_parsed (source->priv->db, GTK_TREE_MODEL (source->priv->model), query);
 }
diff -b -u -r1.16 rb-playlist-source.h
--- sources/rb-playlist-source.h	16 Sep 2004 19:16:07 -0000	1.16
+++ sources/rb-playlist-source.h	23 Aug 2005 14:51:41 -0000
@@ -63,17 +63,19 @@
 RBSource *	rb_playlist_source_new_from_xml	(RBShell *shell,
 						 xmlNodePtr node);
 
-RhythmDBQueryModel *rb_playlist_source_get_model(RBPlaylistSource *source);
-
 void		rb_playlist_source_set_query	(RBPlaylistSource *source,
 						 GPtrArray *query,
 						 guint limit_count,
-						 guint limit_mb);
+						 guint limit_mb,
+						 const char *sort_key,
+						 gint sort_order);
 
 void		rb_playlist_source_get_query	(RBPlaylistSource *source,
 						 GPtrArray **query,
 						 guint *limit_count,
-						 guint *limit_mb);
+						 guint *limit_mb,
+						 const char **sort_key,
+						 gint *sort_order);
 
 void		rb_playlist_source_save_playlist(RBPlaylistSource *source,
 						 const char *uri);
diff -b -u -r1.20 Makefile.am
--- widgets/Makefile.am	17 Aug 2005 16:32:54 -0000	1.20
+++ widgets/Makefile.am	23 Aug 2005 14:51:50 -0000
@@ -40,7 +40,9 @@
 	rb-rating-helper.c				\
 	rb-rating-helper.h				\
 	rb-query-creator.c				\
-	rb-query-creator.h
+	rb-query-creator.h				\
+	rb-query-creator-private.h			\
+	rb-query-creator-properties.c
 
 INCLUDES =						\
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
diff -b -u -r1.92 rb-entry-view.c
--- widgets/rb-entry-view.c	15 Aug 2005 13:37:53 -0000	1.92
+++ widgets/rb-entry-view.c	23 Aug 2005 14:51:58 -0000
@@ -1091,15 +1091,18 @@
 void 
 rb_entry_view_get_sorting_order (RBEntryView *view, const char **column_name, gint *sort_order)
 {
-	*column_name = g_strdup(view->priv->sorting_column_name);
+	*column_name = g_strdup (view->priv->sorting_column_name);
 	*sort_order = view->priv->sorting_order;
 }
 
 void
 rb_entry_view_set_sorting_order (RBEntryView *view, const char *column_name, gint sort_order)
 {
-	g_free(view->priv->sorting_column_name);
-	view->priv->sorting_column_name = g_strdup(column_name);
+	if (column_name == NULL)
+		return;
+
+	g_free (view->priv->sorting_column_name);
+	view->priv->sorting_column_name = g_strdup (column_name);
 	view->priv->sorting_order = sort_order;
 	
 	rb_entry_view_sync_sorting (view);
diff -b -u -r1.29 rb-query-creator.c
--- widgets/rb-query-creator.c	25 Jul 2005 16:30:58 -0000	1.29
+++ widgets/rb-query-creator.c	23 Aug 2005 14:52:06 -0000
@@ -27,56 +27,17 @@
 
 #include "rhythmdb.h"
 #include "rb-query-creator.h"
+#include "rb-query-creator-private.h"
 #include "rb-dialog.h"
-#include "rb-rating.h"
 #include "rb-debug.h"
 #include "rb-preferences.h"
 #include "rb-glade-helpers.h"
-#include "eel-gconf-extensions.h"
 
-typedef struct
-{
-	const char *name;
-	RhythmDBPropType strict_val;
-	RhythmDBPropType fuzzy_val;
-} RBQueryCreatorPropertyOption;
-
-typedef struct
-{
-	const char *name;
-	gboolean strict;
-	RhythmDBPropType val;
-} RBQueryCreatorCriteriaOption;
-
-const RBQueryCreatorPropertyOption property_options[] =
-{
-	{ N_("Title"), RHYTHMDB_PROP_TITLE, RHYTHMDB_PROP_TITLE_FOLDED },
-	{ N_("Artist"), RHYTHMDB_PROP_ARTIST, RHYTHMDB_PROP_ARTIST_FOLDED },
-	{ N_("Album"), RHYTHMDB_PROP_ALBUM, RHYTHMDB_PROP_ALBUM_FOLDED },
-	{ N_("Genre"), RHYTHMDB_PROP_GENRE, RHYTHMDB_PROP_GENRE_FOLDED },
-	{ N_("Rating"), RHYTHMDB_PROP_RATING, RHYTHMDB_PROP_RATING },
-	{ N_("Path"), RHYTHMDB_PROP_LOCATION, RHYTHMDB_PROP_LOCATION }
-};
-
-const RBQueryCreatorCriteriaOption string_criteria_options[] =
-{
-	{ N_("contains"), 0, RHYTHMDB_QUERY_PROP_LIKE },
-	{ N_("does not contain"), 0, RHYTHMDB_QUERY_PROP_NOT_LIKE },
-	{ N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS }
-};
-
-const RBQueryCreatorCriteriaOption numeric_criteria_options[] =
-{
-	{ N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS },
-	{ N_("at least"), 1, RHYTHMDB_QUERY_PROP_GREATER },	/* matches if A >= B */
-	{ N_("at most"), 1, RHYTHMDB_QUERY_PROP_LESS }		/* matches if A <= B */
-};
 
 static void rb_query_creator_class_init (RBQueryCreatorClass *klass);
-static void rb_query_creator_init (RBQueryCreator *dlg);
 static GObject *rb_query_creator_constructor (GType type, guint n_construct_properties,
 					      GObjectConstructParam *construct_properties);
-static void rb_query_creator_finalize (GObject *object);
+static void rb_query_creator_dispose (GObject *object);
 static void rb_query_creator_set_property (GObject *object,
 					      guint prop_id,
 					      const GValue *value,
@@ -85,35 +46,30 @@
 					      guint prop_id,
 					      GValue *value,
 					      GParamSpec *pspec);
-static void select_property_from_value (GtkWidget *option_menu,
-					const RBQueryCreatorPropertyOption *options,
-					int length, RhythmDBPropType prop);
 static void select_criteria_from_value (RBQueryCreator *creator,
 					GtkWidget *option_menu,
 					RhythmDBPropType prop,
 					RhythmDBQueryType qtype);
-
-static void setup_property_option_menu (RBQueryCreator *creator,
-					GtkWidget *option_menu,
-					const RBQueryCreatorPropertyOption *options,
-					int length, gboolean activate_first);
-static void setup_criteria_option_menu (GtkWidget *option_menu,
-					const RBQueryCreatorCriteriaOption *options,
-					int length, gboolean activate_first);
 static GtkWidget * create_property_option_menu (RBQueryCreator *creator,
 						const RBQueryCreatorPropertyOption *options,
-						int length, gboolean activate_first);
+						int length);
 static GtkWidget * create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
-						int length, gboolean activate_first);
-static GtkWidget * option_menu_get_active_child (GtkWidget *option_menu);
-static GtkWidget * get_box_widget_at_pos (GtkBox *box, guint pos);
+						int length);
+static void setup_sort_option_menu (RBQueryCreator *creator,
+				    GtkWidget *option_menu,
+				    const RBQueryCreatorSortOption *options,
+				    int length);
 
-static GtkWidget * append_row (RBQueryCreator *dialog);
+static GtkWidget * append_row (RBQueryCreator *creator);
 static void add_button_click_cb (GtkWidget *button, RBQueryCreator *creator);
 static void remove_button_click_cb (GtkWidget *button, RBQueryCreator *creator);
 static void limit_toggled_cb (GtkWidget *limit, RBQueryCreator *creator);
 
-struct RBQueryCreatorPrivate
+static int get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options,
+			    	  int length, RhythmDBPropType prop);
+static void sort_option_menu_changed (GtkOptionMenu *propmenu, RBQueryCreator *creator);
+
+typedef struct
 {
 	RhythmDB *db;
 
@@ -132,8 +88,14 @@
 	GtkWidget *limit_check;
 	GtkWidget *limit_entry;
 	GtkWidget *limit_option;
-	GPtrArray *queries;
-};
+	GtkWidget *sort_menu;
+	GtkWidget *sort_desc;
+} RBQueryCreatorPrivate;
+
+
+static GType rb_query_creator_type = 0;
+
+#define QUERY_CREATOR_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), rb_query_creator_type, RBQueryCreatorPrivate))
 
 static GObjectClass *parent_class = NULL;
 
@@ -147,8 +109,6 @@
 GType
 rb_query_creator_get_type (void)
 {
-	static GType rb_query_creator_type = 0;
-
 	if (rb_query_creator_type == 0)
 	{
 		static const GTypeInfo our_info =
@@ -161,7 +121,7 @@
 			NULL,
 			sizeof (RBQueryCreator),
 			0,
-			(GInstanceInitFunc) rb_query_creator_init
+			NULL
 		};
 		
 		rb_query_creator_type = g_type_register_static (GTK_TYPE_DIALOG,
@@ -179,7 +139,7 @@
 
 	parent_class = g_type_class_peek_parent (klass);
 
-	object_class->finalize = rb_query_creator_finalize;
+	object_class->dispose = rb_query_creator_dispose;
 	object_class->constructor = rb_query_creator_constructor;
 	object_class->set_property = rb_query_creator_set_property;
 	object_class->get_property = rb_query_creator_get_property;
@@ -199,13 +159,16 @@
 							       "Whether or not we're creating a new playlist",
 							       TRUE,
 							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_type_class_add_private (klass, sizeof (RBQueryCreatorPrivate));
 }
 
 static GObject *
 rb_query_creator_constructor (GType type, guint n_construct_properties,
 			      GObjectConstructParam *construct_properties)
 {
-	RBQueryCreator *dlg;
+	RBQueryCreatorPrivate *priv;
+	RBQueryCreator *creator;
 	RBQueryCreatorClass *klass;
 	GObjectClass *parent_class;  
 	GladeXML *xml;
@@ -213,92 +176,100 @@
 
 	klass = RB_QUERY_CREATOR_CLASS (g_type_class_peek (type));
 	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
-	dlg = RB_QUERY_CREATOR (parent_class->constructor (type, n_construct_properties,
+	creator = RB_QUERY_CREATOR (parent_class->constructor (type, n_construct_properties,
 							   construct_properties));
+	priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	
-	if (dlg->priv->creating) { 
-		gtk_dialog_add_button (GTK_DIALOG (dlg),
+	if (priv->creating) { 
+		gtk_dialog_add_button (GTK_DIALOG (creator),
 				       GTK_STOCK_CANCEL,
 				       GTK_RESPONSE_CLOSE);
-		gtk_dialog_add_button (GTK_DIALOG (dlg),
+		gtk_dialog_add_button (GTK_DIALOG (creator),
 				       GTK_STOCK_NEW,
 				       GTK_RESPONSE_OK);
 	} else {
-		gtk_dialog_add_button (GTK_DIALOG (dlg),
+		gtk_dialog_add_button (GTK_DIALOG (creator),
 				       GTK_STOCK_CLOSE,
 				       GTK_RESPONSE_CLOSE);
 	}
-	gtk_dialog_set_default_response (GTK_DIALOG (dlg),
+	gtk_dialog_set_default_response (GTK_DIALOG (creator),
 					 GTK_RESPONSE_CLOSE);
 
-	dlg->priv->property_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-	dlg->priv->criteria_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-	dlg->priv->entry_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-	dlg->priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-
-	gtk_window_set_title (GTK_WINDOW (dlg), _("Create Automatic Playlist"));
-
-	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);
-	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dlg)->vbox), 2);
-	gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE);
+	priv->property_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+	priv->criteria_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+	priv->entry_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+	priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+	if (priv->creating)
+		gtk_window_set_title (GTK_WINDOW (creator), _("Create Automatic Playlist"));
+	else
+		gtk_window_set_title (GTK_WINDOW (creator), _("Edit Automatic Playlist"));
+
+	gtk_container_set_border_width (GTK_CONTAINER (creator), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (creator)->vbox), 2);
+	gtk_dialog_set_has_separator (GTK_DIALOG (creator), FALSE);
 
 	xml = rb_glade_xml_new ("create-playlist.glade",
 				"main_vbox",
-				dlg);
+				creator);
 	
-	dlg->priv->disjunction_check = GTK_WIDGET (glade_xml_get_widget (xml, "disjunctionCheck"));
-	dlg->priv->limit_check = GTK_WIDGET (glade_xml_get_widget (xml, "limitCheck"));
-	dlg->priv->limit_entry = GTK_WIDGET (glade_xml_get_widget (xml, "limitEntry"));
-	dlg->priv->limit_option = GTK_WIDGET (glade_xml_get_widget (xml, "limitOption"));
-	dlg->priv->addbutton = GTK_WIDGET (glade_xml_get_widget (xml, "addButton"));
-
-	g_signal_connect_object (G_OBJECT (dlg->priv->limit_check), "toggled", G_CALLBACK (limit_toggled_cb),
-				 dlg, 0);
-	gtk_widget_set_sensitive (dlg->priv->limit_entry, FALSE);
-	gtk_widget_set_sensitive (dlg->priv->limit_option, FALSE);
-
-	gtk_size_group_add_widget (dlg->priv->button_size_group, dlg->priv->addbutton);
-	g_signal_connect_object (G_OBJECT (dlg->priv->addbutton), "clicked", G_CALLBACK (add_button_click_cb),
-				 dlg, 0);
+	priv->disjunction_check = GTK_WIDGET (glade_xml_get_widget (xml, "disjunctionCheck"));
+	priv->limit_check = GTK_WIDGET (glade_xml_get_widget (xml, "limitCheck"));
+	priv->limit_entry = GTK_WIDGET (glade_xml_get_widget (xml, "limitEntry"));
+	priv->limit_option = GTK_WIDGET (glade_xml_get_widget (xml, "limitOption"));
+	priv->addbutton = GTK_WIDGET (glade_xml_get_widget (xml, "addButton"));
+	priv->sort_menu = GTK_WIDGET (glade_xml_get_widget (xml, "sortMenu"));
+	priv->sort_desc = GTK_WIDGET (glade_xml_get_widget (xml, "sortDesc"));
+
+	g_signal_connect_object (G_OBJECT (priv->limit_check), "toggled", G_CALLBACK (limit_toggled_cb),
+				 creator, 0);
+	limit_toggled_cb (priv->limit_check, creator);
+
+	gtk_size_group_add_widget (priv->button_size_group, priv->addbutton);
+	g_signal_connect_object (G_OBJECT (priv->addbutton), "clicked", G_CALLBACK (add_button_click_cb),
+				 creator, 0);
 	
-	dlg->priv->vbox = GTK_BOX (glade_xml_get_widget (xml, "sub_vbox"));
-	append_row(dlg);
+	setup_sort_option_menu (creator, priv->sort_menu, sort_options, num_sort_options);
 
-	mainbox = glade_xml_get_widget (xml, "main_vbox");
-	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), mainbox, FALSE, FALSE, 0);
-	gtk_widget_show_all (GTK_WIDGET (dlg));
+	priv->vbox = GTK_BOX (glade_xml_get_widget (xml, "sub_vbox"));
+	append_row (creator);
 
-	return G_OBJECT (dlg);
-}
+	mainbox = glade_xml_get_widget (xml, "main_vbox");
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (creator)->vbox), mainbox, FALSE, FALSE, 0);
+	gtk_widget_show_all (GTK_WIDGET (creator));
 
-static void
-rb_query_creator_init (RBQueryCreator *dlg)
-{
-	dlg->priv = g_new0 (RBQueryCreatorPrivate, 1);
+	return G_OBJECT (creator);
 }
 
 static void
-rb_query_creator_finalize (GObject *object)
+rb_query_creator_dispose (GObject *object)
 {
-	RBQueryCreator *dlg;
+	RBQueryCreator *creator;
+	RBQueryCreatorPrivate *priv;
 
-	g_return_if_fail (object != NULL);
 	g_return_if_fail (RB_IS_QUERY_CREATOR (object));
 
-	dlg = RB_QUERY_CREATOR (object);
-
-	g_return_if_fail (dlg->priv != NULL);
-
-	g_object_unref (G_OBJECT (dlg->priv->property_size_group));
-	g_object_unref (G_OBJECT (dlg->priv->criteria_size_group));
-	g_object_unref (G_OBJECT (dlg->priv->entry_size_group));
-	g_object_unref (G_OBJECT (dlg->priv->button_size_group));
+	priv = QUERY_CREATOR_GET_PRIVATE (object);
+	g_return_if_fail (priv != NULL);
 
-	g_list_free (dlg->priv->rows);
+	if (priv->property_size_group != NULL)
+		g_object_unref (G_OBJECT (priv->property_size_group));
+	priv->property_size_group = NULL;
+	if (priv->criteria_size_group != NULL)
+		g_object_unref (G_OBJECT (priv->criteria_size_group));
+	priv->criteria_size_group = NULL;
+	if (priv->entry_size_group != NULL)
+		g_object_unref (G_OBJECT (priv->entry_size_group));
+	priv->entry_size_group = NULL;
+	if (priv->button_size_group != NULL)
+		g_object_unref (G_OBJECT (priv->button_size_group));
+	priv->button_size_group = NULL;
+
+	if (priv->rows)
+		g_list_free (priv->rows);
+	priv->rows = NULL;
 	
-	g_free (dlg->priv);
-
-	G_OBJECT_CLASS (parent_class)->finalize (object);
+	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
@@ -307,15 +278,16 @@
 			       const GValue *value,
 			       GParamSpec *pspec)
 {
-	RBQueryCreator *dlg = RB_QUERY_CREATOR (object);
+	RBQueryCreator *creator = RB_QUERY_CREATOR (object);
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 
 	switch (prop_id)
 	{
 	case PROP_DB:
-		dlg->priv->db = g_value_get_object (value);
+		priv->db = g_value_get_object (value);
 		break;
 	case PROP_CREATING:
-		dlg->priv->creating = g_value_get_boolean (value);
+		priv->creating = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -329,15 +301,16 @@
 			      GValue *value,
 			      GParamSpec *pspec)
 {
-	RBQueryCreator *dlg = RB_QUERY_CREATOR (object);
+	RBQueryCreator *creator = RB_QUERY_CREATOR (object);
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 
 	switch (prop_id)
 	{
 	case PROP_DB:
-		g_value_set_object (value, dlg->priv->db);
+		g_value_set_object (value, priv->db);
 		break;
 	case PROP_CREATING:
-		g_value_set_boolean (value, dlg->priv->creating);
+		g_value_set_boolean (value, priv->creating);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -352,20 +325,21 @@
 	return g_object_new (RB_TYPE_QUERY_CREATOR, "db", db, NULL);
 }
 
-static void
+static gboolean
 rb_query_creator_load_query (RBQueryCreator *creator, GPtrArray *query,
 			     int limit_count, int limit_size)
 {
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	int i;
 	GList *rows;
 	gboolean disjunction = FALSE;
 	RhythmDBQueryData *qdata;
 	GPtrArray *subquery;
 
-	g_assert (query->len == 2);
+	g_return_val_if_fail (query->len == 2, FALSE);
 
 	qdata = g_ptr_array_index (query, 1);
-	g_assert (qdata->type == RHYTHMDB_QUERY_SUBQUERY);
+	g_return_val_if_fail (qdata->type == RHYTHMDB_QUERY_SUBQUERY, FALSE);
 
 	subquery = qdata->subquery;
 
@@ -376,12 +350,14 @@
 				append_row (creator);
 		}
 	
-	rows = creator->priv->rows;
+	rows = priv->rows;
 
 	for (i = 0; i < subquery->len; i++) {
 		RhythmDBQueryData *data = g_ptr_array_index (subquery, i);
 		GtkOptionMenu *propmenu;
-		GtkOptionMenu *criteria_menu;
+		GtkWidget *criteria_menu;
+		int index;
+		const RBQueryCreatorPropertyType *property_type;
 
 		if (data->type == RHYTHMDB_QUERY_DISJUNCTION) {
 			disjunction = TRUE;
@@ -389,60 +365,94 @@
 		}
 
 		propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (rows->data), 0));
+		index = get_property_index_from_proptype (property_options, num_property_options, data->propid);
+		gtk_option_menu_set_history (propmenu, index);
 		
-		select_property_from_value (GTK_WIDGET (propmenu), property_options,
-					    G_N_ELEMENTS (property_options), data->propid);
+		criteria_menu = get_box_widget_at_pos (GTK_BOX (rows->data), 1);
+		select_criteria_from_value (creator, criteria_menu, data->propid, data->type);
 
-		criteria_menu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (rows->data),
-									1));
+		property_type = property_options[index].property_type;
+		g_assert (property_type->criteria_set_widget_data != NULL);
+		property_type->criteria_set_widget_data (get_box_widget_at_pos (GTK_BOX (rows->data), 2),
+					      data->val);
+			
+		rows = rows->next;
+	}
 
-		select_criteria_from_value (creator,
-					    GTK_WIDGET (criteria_menu), data->propid,
-					    data->type);
+	/* setup the limits */
+	{
+		int limit;
 
-		if (data->propid != RHYTHMDB_PROP_RATING) {
-			GtkEntry *text = GTK_ENTRY (get_box_widget_at_pos (GTK_BOX (rows->data), 2));
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->disjunction_check),
+				      disjunction);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->limit_check),
+				      limit_count || limit_size);
 			
-			gtk_entry_set_text (text, g_value_get_string (data->val));
+	if (limit_count) {
+		gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 0);
+			limit = limit_count;
+		} else if (limit_size % 1000 == 0) {
+		gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 2);
+			limit = limit_size / 1000;
 		} else {
-			RBRating *rating = RB_RATING (get_box_widget_at_pos (GTK_BOX (rows->data), 2));
-			g_object_set (G_OBJECT (rating), "rating",
-				      g_value_get_double (data->val), NULL);
+			gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 1);
+			limit = limit_size;
 		}
-		rows = rows->next;
+
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->limit_entry), limit);
 	}
 
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (creator->priv->disjunction_check),
-				      disjunction);
+	return TRUE;
+}
 
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (creator->priv->limit_check),
-				      limit_count || limit_size);
-	if (limit_count) {
-		gtk_option_menu_set_history (GTK_OPTION_MENU (creator->priv->limit_option), 0);
-		gtk_spin_button_set_value (GTK_SPIN_BUTTON (creator->priv->limit_entry), limit_count);
-	} else if (limit_size > 0 && limit_size < 1000) {
-		gtk_option_menu_set_history (GTK_OPTION_MENU (creator->priv->limit_option), 1);
-		gtk_spin_button_set_value (GTK_SPIN_BUTTON (creator->priv->limit_entry), limit_size);
-	} else if (limit_size >= 1000) {
-		gtk_option_menu_set_history (GTK_OPTION_MENU (creator->priv->limit_option), 2);
-		gtk_spin_button_set_value (GTK_SPIN_BUTTON (creator->priv->limit_entry), limit_size / 1000);
+static gboolean
+rb_query_creator_set_sorting (RBQueryCreator *creator, const char *sort_column, gint sort_direction)
+{
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
+	int i;
+
+	if (!sort_column || ! *sort_column) {
+		g_warning("No playlist sorting order");
+
+		sort_column = sort_options[DEFAULT_SORTING_COLUMN].sort_key;
+		sort_direction = DEFAULT_SORTING_ORDER;
 	}
+
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sort_desc), (sort_direction == GTK_SORT_DESCENDING));
+
+	for (i = 0; i < num_property_options; i++)
+		if (strcmp (sort_options[i].sort_key, sort_column) == 0)
+			break;
+
+	/* check that it is a valid sort option */
+	g_return_val_if_fail (i < num_property_options, FALSE);
+
+	gtk_option_menu_set_history (GTK_OPTION_MENU (priv->sort_menu), i);
+	sort_option_menu_changed (GTK_OPTION_MENU (priv->sort_menu), creator); /* force the checkbox to change label */
+	return TRUE;
 }
 
 GtkWidget *
 rb_query_creator_new_from_query (RhythmDB *db, GPtrArray *query,
-				 int limit_count, int limit_size)
+				 int limit_count, int limit_size,
+				 const char *sort_column, gint sort_direction)
 {
 	RBQueryCreator *creator = g_object_new (RB_TYPE_QUERY_CREATOR, "db", db,
 						"creating", FALSE, NULL);
 	if (!creator)
 		return NULL;
 
-	rb_query_creator_load_query (creator, query, limit_count, limit_size);
+	if ( !rb_query_creator_load_query (creator, query, limit_count, limit_size)
+	   | !rb_query_creator_set_sorting (creator, sort_column, sort_direction)) {
+		gtk_widget_destroy (GTK_WIDGET (creator));
+		return NULL;
+	}
+
+
 	return GTK_WIDGET (creator);
 }
 
-static GtkWidget *
+GtkWidget *
 get_box_widget_at_pos (GtkBox *box, guint pos)
 {
 	GtkWidget *ret = NULL;
@@ -463,141 +473,69 @@
 	return GTK_WIDGET (ret);
 }
 
-static const RBQueryCreatorCriteriaOption *
-get_criteria_for_property (RBQueryCreator *creator, RhythmDBPropType prop, guint *length)
-{
-	GType proptype;
-	const RBQueryCreatorCriteriaOption *options = NULL;
-
-	proptype = rhythmdb_get_property_type (creator->priv->db, prop);
-
-	if (proptype == G_TYPE_STRING) {
-		options = string_criteria_options;
-		if (length)
-			*length = G_N_ELEMENTS (string_criteria_options);
-	} else if (proptype == G_TYPE_DOUBLE) {
-		options = numeric_criteria_options;
-		if (length)
-			*length = G_N_ELEMENTS (numeric_criteria_options);
-	} else
-		g_assert_not_reached ();
-	return options;
-}
-
-static void
-set_rating_score (RBRating *rating, gdouble score)
-{
-	g_object_set (G_OBJECT (rating), "rating", score, NULL);
-}	
-
 static GtkWidget *
 get_entry_for_property (RBQueryCreator *creator, RhythmDBPropType prop,
 			gboolean *constrain)
 {
-	GType proptype;
-	proptype = rhythmdb_get_property_type (creator->priv->db, prop);
-	
-	*constrain = TRUE;
+	const RBQueryCreatorPropertyType *property_type;
+	int index = get_property_index_from_proptype (property_options, num_property_options, prop);
 
-	if (proptype == G_TYPE_STRING)
-		return gtk_entry_new ();
-	else if (prop == RHYTHMDB_PROP_RATING) {
-		RBRating *rating = rb_rating_new ();
-		g_signal_connect_object (G_OBJECT (rating), "rated",
-					 G_CALLBACK (set_rating_score), NULL, 0);
-		*constrain = FALSE;
-		return GTK_WIDGET (rating);
-	} else
-		g_assert_not_reached ();
-	return NULL;
-}
+	property_type = property_options[index].property_type;
+	g_assert (property_type->criteria_create_widget != NULL);
 
-static int
-extract_option_menu_index (GtkOptionMenu *menu)
-{
-	GtkWidget *active_item = option_menu_get_active_child (GTK_WIDGET (menu));
-	return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (active_item), "rb-query-creator-value"));
+	*constrain = TRUE;
+	return property_type->criteria_create_widget (constrain);
 }
 
 GPtrArray *
-rb_query_creator_get_query (RBQueryCreator *dlg)
+rb_query_creator_get_query (RBQueryCreator *creator)
 {
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	GPtrArray *query;
 	GPtrArray *sub_query;
 	GList *rows, *row;
 	gboolean disjunction;
 
-	query = rhythmdb_query_parse (dlg->priv->db,
-				      RHYTHMDB_QUERY_PROP_EQUALS,
-				      RHYTHMDB_PROP_TYPE,
-				      RHYTHMDB_ENTRY_TYPE_SONG,
-				      RHYTHMDB_QUERY_END);
-
-	disjunction = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dlg->priv->disjunction_check));
-	
+	disjunction = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->disjunction_check));
 	sub_query = g_ptr_array_new ();
+	rows = priv->rows;
 	
-	rows = dlg->priv->rows;
 	for (row = rows; row; row = row->next) {
 		GtkOptionMenu *propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data),
 										  0));
 		GtkOptionMenu *criteria_menu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data),
 										       1));
-		guint prop_position = extract_option_menu_index (propmenu);
+		guint prop_position = gtk_option_menu_get_history (propmenu);
 		const RBQueryCreatorPropertyOption *prop_option = &property_options[prop_position];
-		const RBQueryCreatorCriteriaOption *criteria_options = get_criteria_for_property (dlg, prop_option->strict_val, NULL);
-		const RBQueryCreatorCriteriaOption *criteria_option = &criteria_options[extract_option_menu_index (criteria_menu)];
-		RhythmDBQueryType criteria = criteria_option->val;
-		RhythmDBPropType prop = criteria_option->strict ? prop_option->strict_val : prop_option->fuzzy_val;
-
-		if (prop != RHYTHMDB_PROP_RATING) {
-			GtkEntry *text = GTK_ENTRY (get_box_widget_at_pos (GTK_BOX (row->data), 2));
-			const char *data = gtk_entry_get_text (GTK_ENTRY (text));
-			char *folded_data = criteria_option->strict ? g_strdup (data) : g_utf8_casefold (data, -1);
+		const RBQueryCreatorCriteriaOption *criteria_options = prop_option->property_type->criteria_options;
+		const RBQueryCreatorCriteriaOption *criteria_option = &criteria_options[gtk_option_menu_get_history (criteria_menu)];
 			
-			/* ignore the row if the string is empty */
-			if (folded_data && *folded_data) {
-				if (disjunction && row->next)
-					rhythmdb_query_append (dlg->priv->db,
-							       sub_query,
-							       criteria,
-							       prop,
-							       folded_data,
-							       RHYTHMDB_QUERY_DISJUNCTION,
-							       RHYTHMDB_QUERY_END);
-				else
-					rhythmdb_query_append (dlg->priv->db,
-							       sub_query,
-							       criteria,
-							       prop,
-							       folded_data,
-							       RHYTHMDB_QUERY_END);
+		g_assert (prop_option->property_type->criteria_get_widget_data != NULL);
+		{
+			RhythmDBQueryData *data = g_new0 (RhythmDBQueryData, 1);
+			GValue *val = g_new0 (GValue, 1);
+			
+			data->type = criteria_option->val;
+			data->propid = criteria_option->strict ? prop_option->strict_val : prop_option->fuzzy_val;
+
+			prop_option->property_type->criteria_get_widget_data (get_box_widget_at_pos (GTK_BOX (row->data), 2), val);
+			data->val = val;
+
+			g_ptr_array_add (sub_query, data);
 			}
-			g_free (folded_data);
-		} else {
-			double rating;
 
-			g_object_get (G_OBJECT (get_box_widget_at_pos (GTK_BOX (row->data), 2)),
-				      "rating", &rating, NULL);
 			if (disjunction && row->next)
-				rhythmdb_query_append (dlg->priv->db,
+				rhythmdb_query_append (priv->db,
 						       sub_query,
-						       criteria,
-						       prop,
-						       rating,
 						       RHYTHMDB_QUERY_DISJUNCTION,
 						       RHYTHMDB_QUERY_END);
-			else
-				rhythmdb_query_append (dlg->priv->db,
-						       sub_query,
-						       criteria,
-						       prop,
-						       rating,
-						       RHYTHMDB_QUERY_END);
 		}
-	}
-	rhythmdb_query_append (dlg->priv->db,
-			       query,
+	query = rhythmdb_query_parse (priv->db,
+				      /* type=songs */
+				      RHYTHMDB_QUERY_PROP_EQUALS,
+				      RHYTHMDB_PROP_TYPE,
+				      RHYTHMDB_ENTRY_TYPE_SONG,
+				      /* the constructed query */
 			       RHYTHMDB_QUERY_SUBQUERY,
 			       sub_query,
 			       RHYTHMDB_QUERY_END);
@@ -605,15 +543,17 @@
 }
 
 void
-rb_query_creator_get_limit (RBQueryCreator *dlg, RBQueryCreatorLimitType *type,
+rb_query_creator_get_limit (RBQueryCreator *creator, RBQueryCreatorLimitType *type,
 			    guint *limit)
 {
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	guint limitpos;
-	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dlg->priv->limit_check)))
-		*limit = (guint) gtk_spin_button_get_value(GTK_SPIN_BUTTON (dlg->priv->limit_entry));
+
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->limit_check)))
+		*limit = (guint) gtk_spin_button_get_value(GTK_SPIN_BUTTON (priv->limit_entry));
 	else
 		*limit = 0;
-	limitpos = gtk_option_menu_get_history (GTK_OPTION_MENU (dlg->priv->limit_option));
+	limitpos = gtk_option_menu_get_history (GTK_OPTION_MENU (priv->limit_option));
 	switch (limitpos)
 	{
 	case 0:
@@ -631,19 +571,38 @@
 	}
 }
 
+void
+rb_query_creator_get_sort_order (RBQueryCreator *creator, const char **sort_column, gint *sort_direction)
+{
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
+	int i;
+
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->sort_desc)))
+		*sort_direction = GTK_SORT_DESCENDING;
+	else
+		*sort_direction = GTK_SORT_ASCENDING;
+
+	i = gtk_option_menu_get_history (GTK_OPTION_MENU (priv->sort_menu));
+	*sort_column = sort_options[i].sort_key;
+}
+
 static void
-limit_toggled_cb (GtkWidget *limit, RBQueryCreator *dialog)
+limit_toggled_cb (GtkWidget *limit, RBQueryCreator *creator)
 {
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (limit));
 
-	gtk_widget_set_sensitive (dialog->priv->limit_entry, active);
-	gtk_widget_set_sensitive (dialog->priv->limit_option, active);
+	gtk_widget_set_sensitive (priv->limit_entry, active);
+	gtk_widget_set_sensitive (priv->limit_option, active);
+	gtk_widget_set_sensitive (priv->sort_menu, active);
+	gtk_widget_set_sensitive (priv->sort_desc, active);
 }
 
 static GtkWidget *
-lookup_row_by_widget (RBQueryCreator *dialog, GtkWidget *widget)
+lookup_row_by_widget (RBQueryCreator *creator, GtkWidget *widget)
 {
-	GList *rows = dialog->priv->rows;
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
+	GList *rows = priv->rows;
 	GList *row;
 	GtkWidget *ret = NULL;
 	guint i;
@@ -661,15 +620,16 @@
 }
 
 static void
-remove_button_click_cb (GtkWidget *button, RBQueryCreator *dialog)
+remove_button_click_cb (GtkWidget *button, RBQueryCreator *creator)
 {
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	GtkWidget *row;
 
-	row = lookup_row_by_widget (dialog, button);
+	row = lookup_row_by_widget (creator, button);
 	g_assert (row);
-	gtk_container_remove (GTK_CONTAINER (dialog->priv->vbox),
+	gtk_container_remove (GTK_CONTAINER (priv->vbox),
 			      GTK_WIDGET (row));
-	dialog->priv->rows = g_list_remove (dialog->priv->rows, row);
+	priv->rows = g_list_remove (priv->rows, row);
 }
 
 static void
@@ -679,8 +639,9 @@
 }
 
 static GtkWidget *
-append_row (RBQueryCreator *dialog)
+append_row (RBQueryCreator *creator)
 {
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	GtkWidget *option;
 	GtkWidget *criteria;
 	GtkWidget *entry;
@@ -688,58 +649,55 @@
 	GtkBox *hbox;
 	GList *rows;
 	guint len;
+	gboolean constrain;
 
-	rows = dialog->priv->rows;
+	rows = priv->rows;
 	len = g_list_length (rows);
 
 	hbox = GTK_BOX (gtk_hbox_new (FALSE, 5));
-	gtk_box_pack_start_defaults (GTK_BOX (dialog->priv->vbox), GTK_WIDGET (hbox));
-	dialog->priv->rows = g_list_prepend (dialog->priv->rows, hbox);
-	gtk_box_reorder_child (dialog->priv->vbox, GTK_WIDGET (hbox), -1);
+	gtk_box_pack_start_defaults (GTK_BOX (priv->vbox), GTK_WIDGET (hbox));
+	priv->rows = g_list_prepend (priv->rows, hbox);
+	gtk_box_reorder_child (priv->vbox, GTK_WIDGET (hbox), -1);
 
 	/* This is the main (leftmost) GtkOptionMenu, for types. */
-	option = create_property_option_menu (dialog, property_options,
-					      G_N_ELEMENTS (property_options),
-					      FALSE);
-	gtk_size_group_add_widget (dialog->priv->property_size_group, option);
+	option = create_property_option_menu (creator, property_options, num_property_options);
+	gtk_size_group_add_widget (priv->property_size_group, option);
 	gtk_box_pack_start_defaults (hbox, GTK_WIDGET (option));
 	gtk_option_menu_set_history (GTK_OPTION_MENU (option), 0);
-	criteria = create_criteria_option_menu (string_criteria_options,
-						G_N_ELEMENTS (string_criteria_options),
-						FALSE);
-	gtk_size_group_add_widget (dialog->priv->criteria_size_group, criteria);
+	criteria = create_criteria_option_menu (property_options[0].property_type->criteria_options,
+						property_options[0].property_type->num_criteria_options);
+	gtk_size_group_add_widget (priv->criteria_size_group, criteria);
 	gtk_box_pack_start_defaults (hbox, GTK_WIDGET (criteria));
 
-	entry = gtk_entry_new ();
-	gtk_size_group_add_widget (dialog->priv->entry_size_group, entry);
+	entry = get_entry_for_property (creator, property_options[0].strict_val, &constrain);
+	if (constrain)
+		gtk_size_group_add_widget (priv->entry_size_group, entry);
 	gtk_box_pack_start_defaults (hbox, GTK_WIDGET (entry));
 
 	remove_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
 	g_signal_connect_object (G_OBJECT (remove_button), "clicked", G_CALLBACK (remove_button_click_cb),
-				 dialog, 0);
-	gtk_size_group_add_widget (dialog->priv->button_size_group, remove_button);
+				 creator, 0);
+	gtk_size_group_add_widget (priv->button_size_group, remove_button);
 	gtk_box_pack_start_defaults (hbox, GTK_WIDGET (remove_button));
 
-	gtk_widget_show_all (GTK_WIDGET (dialog->priv->vbox));
+	gtk_widget_show_all (GTK_WIDGET (priv->vbox));
 	return GTK_WIDGET (hbox);
 }
 
-static void
-select_property_from_value (GtkWidget *option_menu,
-			    const RBQueryCreatorPropertyOption *options,
+static int
+get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options,
 			    int length, RhythmDBPropType prop)
 {
 	int i;
 	
-	for (i = 0; i < length; i++) {
-		if (prop == options[i].strict_val || prop == options[i].fuzzy_val) {
-			gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), i);
-			return;
-		}
-	}
+	for (i = 0; i < length; i++)
+		if (prop == options[i].strict_val || prop == options[i].fuzzy_val)
+			return i;
+
 	g_assert_not_reached ();
 }
 
+
 static void
 select_criteria_from_value (RBQueryCreator *creator,
 			    GtkWidget *option_menu,
@@ -750,7 +708,9 @@
 	const RBQueryCreatorCriteriaOption *options;
 	guint length;
 
-	options = get_criteria_for_property (creator, prop, &length);
+	i = get_property_index_from_proptype (property_options, num_property_options, prop);
+	length = property_options[i].property_type->num_criteria_options;
+	options =  property_options[i].property_type->criteria_options;
 			
 	for (i = 0; i < length; i++) {
 		if (qtype == options[i].val) {
@@ -765,27 +725,37 @@
 property_option_menu_changed (GtkOptionMenu *propmenu,
 			      RBQueryCreator *creator)
 {
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
 	GtkWidget *row;
 	GtkWidget *criteria;
 	GtkWidget *entry;
 	const RBQueryCreatorPropertyOption *prop_option;
 	const RBQueryCreatorCriteriaOption *criteria_options;
 	guint length;
+	guint old_value;
 	gboolean constrain;
 
+	prop_option = &property_options[gtk_option_menu_get_history (propmenu)];
+	old_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (propmenu), "prop-menu old-value"));
+
+	/* don't recreate the criteria menu and entry if they will be the same*/
+	if (prop_option->property_type == property_options[old_value].property_type)
+		return;
+
+	g_object_set_data (G_OBJECT (propmenu), "prop-menu old-value",
+			   GINT_TO_POINTER (gtk_option_menu_get_history (propmenu)));
+
 	row = lookup_row_by_widget (creator, GTK_WIDGET (propmenu));
 
 	criteria = get_box_widget_at_pos (GTK_BOX (row), 1);
 	gtk_container_remove (GTK_CONTAINER (row), criteria);
 
-	prop_option = &property_options[extract_option_menu_index (propmenu)];
-
-	criteria_options = get_criteria_for_property (creator, prop_option->strict_val, &length);
+	criteria_options = prop_option->property_type->criteria_options;
+	length = prop_option->property_type->num_criteria_options;
 
-	criteria = create_criteria_option_menu (criteria_options, length,
-						FALSE);
+	criteria = create_criteria_option_menu (criteria_options, length);
 	gtk_widget_show (criteria);
-	gtk_size_group_add_widget (creator->priv->criteria_size_group, criteria);
+	gtk_size_group_add_widget (priv->criteria_size_group, criteria);
 	gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (criteria));
 	gtk_box_reorder_child (GTK_BOX (row), criteria, 1);
 
@@ -794,120 +764,101 @@
 	entry = get_entry_for_property (creator, prop_option->strict_val,
 					&constrain);
 	gtk_widget_show (entry);
+
 	if (constrain)
-		gtk_size_group_add_widget (creator->priv->entry_size_group, entry);
+		gtk_size_group_add_widget (priv->entry_size_group, entry);
 	gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (entry));
 	gtk_box_reorder_child (GTK_BOX (row), entry, 2);
 }
 
-/* Stolen from jamboree and hacked up */
-static void
-setup_property_option_menu (RBQueryCreator *creator,
-			    GtkWidget *option_menu,
+static GtkWidget*
+create_property_option_menu (RBQueryCreator *creator,
 			    const RBQueryCreatorPropertyOption *options,
-			    int length, gboolean activate_first)
+			     int length)
 {
+	GtkWidget *option_menu;
 	GtkWidget *menu;
 	GtkWidget *menu_item;
 	int i;
 
+	option_menu = gtk_option_menu_new ();
 	menu = gtk_menu_new ();
-	gtk_widget_show (menu);
     
+	/* add the property options */
 	for (i = 0; i < length; i++) {
 		menu_item = gtk_menu_item_new_with_label (_(options[i].name));
-		g_object_set_data (G_OBJECT (menu_item),
-				   "rb-query-creator-value", GINT_TO_POINTER (i));
 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
 		gtk_widget_show (menu_item);
 	}
 
-	if (activate_first)
 		gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), 0);
+	g_object_set_data (G_OBJECT (option_menu), "prop-menu old-value", GINT_TO_POINTER (0));
 
 	gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
+	gtk_widget_show (menu);
 
 	g_signal_connect_object (G_OBJECT (option_menu), "changed",
 				 G_CALLBACK (property_option_menu_changed), creator, 0);
+  
+	return option_menu;
 }
 
-static void
-setup_criteria_option_menu (GtkWidget *option_menu,
-			    const RBQueryCreatorCriteriaOption *options,
-			    int length, gboolean activate_first)
+static GtkWidget*
+create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
+			     int length)
 {
+	GtkWidget *option_menu;
 	GtkWidget *menu;
 	GtkWidget *menu_item;
 	int i;
 
+	option_menu = gtk_option_menu_new ();
 	menu = gtk_menu_new ();
-	gtk_widget_show (menu);
     
 	for (i = 0; i < length; i++) {
 		menu_item = gtk_menu_item_new_with_label (_(options[i].name));
-		g_object_set_data (G_OBJECT (menu_item),
-				   "rb-query-creator-value", GINT_TO_POINTER (i));
 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
 		gtk_widget_show (menu_item);
 	}
 
-	if (activate_first)
-		gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), 0);
-
 	gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
-}
-
-static GtkWidget*
-create_property_option_menu (RBQueryCreator *creator,
-			     const RBQueryCreatorPropertyOption *options,
-			     int length, gboolean activate_first)
-{
-	GtkWidget *menu;
-	GtkWidget *option_menu;
-  
-	menu = gtk_menu_new ();
 	gtk_widget_show (menu);
     
-	option_menu = gtk_option_menu_new ();
+	return option_menu;
+}
 
-	setup_property_option_menu (creator, option_menu, options, length, activate_first);
+static void
+sort_option_menu_changed (GtkOptionMenu *propmenu,
+			  RBQueryCreator *creator)
+{
+	RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
+	int index = gtk_option_menu_get_history (propmenu);
   
-	return option_menu;
+	gtk_button_set_label (GTK_BUTTON (priv->sort_desc), _(sort_options[index].sort_descending_name));
+	rb_debug("changing descending label to %s[%d]", sort_options[index].sort_descending_name, index);
 }
 
-static GtkWidget*
-create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
-			     int length, gboolean activate_first)
+static void
+setup_sort_option_menu (RBQueryCreator *creator,
+			GtkWidget *option_menu,
+			const RBQueryCreatorSortOption *options,
+			int length)
 {
 	GtkWidget *menu;
-	GtkWidget *option_menu;
+	GtkWidget *menu_item;
+	int i;
   
 	menu = gtk_menu_new ();
 	gtk_widget_show (menu);
     
-	option_menu = gtk_option_menu_new ();
-
-	setup_criteria_option_menu (option_menu, options, length, activate_first);
-  
-	return option_menu;
-}
+	for (i = 0; i < length; i++) {
+		menu_item = gtk_menu_item_new_with_label (_(options[i].name));
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+		gtk_widget_show (menu_item);
+	}
 
-static GtkWidget*
-option_menu_get_active_child (GtkWidget *option_menu)
-{
-	GtkWidget *menu;
-	GList *children;
-	int pos;
-	GtkWidget *child;
-  
-	menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (option_menu));
-	children = gtk_container_get_children (GTK_CONTAINER (menu));
-
-	pos = gtk_option_menu_get_history (GTK_OPTION_MENU (option_menu));
-	child = g_list_nth (children, pos)->data;
-	g_assert (child != NULL);
-	g_assert (GTK_IS_WIDGET (child));
+	g_signal_connect_object (G_OBJECT (option_menu), "changed",
+				 G_CALLBACK (sort_option_menu_changed), creator, 0);
   
-	return child;
+	gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
 }
-
diff -b -u -r1.3 rb-query-creator.h
--- widgets/rb-query-creator.h	23 Jan 2004 03:39:18 -0000	1.3
+++ widgets/rb-query-creator.h	23 Aug 2005 14:52:06 -0000
@@ -34,13 +34,9 @@
 #define RB_IS_QUERY_CREATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_QUERY_CREATOR))
 #define RB_QUERY_CREATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_QUERY_CREATOR, RBQueryCreatorClass))
 
-typedef struct RBQueryCreatorPrivate RBQueryCreatorPrivate;
-
 typedef struct
 {
 	GtkDialog parent;
-
-	RBQueryCreatorPrivate *priv;
 } RBQueryCreator;
 
 typedef struct
@@ -59,14 +55,17 @@
 GtkWidget *	rb_query_creator_new		(RhythmDB *db);
 
 GtkWidget *	rb_query_creator_new_from_query	(RhythmDB *db, GPtrArray *query,
-						 int limit_count, int limit_size);
+						 int limit_count, int limit_size,
+						 const char *sort_column, gint sort_direction);
 
 
-GPtrArray *	rb_query_creator_get_query	(RBQueryCreator *dlg);
+GPtrArray *	rb_query_creator_get_query	(RBQueryCreator *creator);
 
-void		rb_query_creator_get_limit	(RBQueryCreator *dlg,
+void		rb_query_creator_get_limit	(RBQueryCreator *creator,
 						 RBQueryCreatorLimitType *type,
 						 guint *limit);
+
+void		rb_query_creator_get_sort_order (RBQueryCreator *creator, const char **sort_key, gint *sort_direction);
 
 G_END_DECLS
 
--- /dev/null	2005-08-18 18:12:49.785368488 +1000
+++ widgets/rb-query-creator-private.h	2005-08-24 00:38:22.000000000 +1000
@@ -0,0 +1,77 @@
+/*
+ *  arch-tag: Header for RhythmDB query creation internals
+ *
+ *  Copyright (C) 2003, 2004 Colin Walters <walters@gnome.org>
+ *  Copyright (C) 2005 James Livingston <walters@gnome.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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 <gtk/gtk.h>
+#include "rhythmdb.h"
+
+#ifndef __RB_QUERY_CREATOR_PRIVATE_H
+#define __RB_QUERY_CREATOR_PRIVATE_H
+
+
+typedef struct
+{
+	const char *name;
+	gboolean strict;
+	RhythmDBPropType val;
+} RBQueryCreatorCriteriaOption;
+
+typedef struct
+{
+	const char *name;
+	const char *sort_key;
+	const char *sort_descending_name;
+} RBQueryCreatorSortOption;
+
+
+typedef GtkWidget*	(*CriteriaCreateWidget)		(gboolean *constrain);
+typedef void		(*CriteriaSetWidgetData)	(GtkWidget *widget, GValue *val);
+typedef void		(*CriteriaGetWidgetData)	(GtkWidget *widget, GValue *val);
+
+typedef struct
+{
+	int num_criteria_options;
+	const RBQueryCreatorCriteriaOption *criteria_options;
+
+	CriteriaCreateWidget	criteria_create_widget;
+	CriteriaSetWidgetData	criteria_set_widget_data;
+	CriteriaGetWidgetData	criteria_get_widget_data;
+} RBQueryCreatorPropertyType;
+
+typedef struct
+{
+	const char *name;
+	RhythmDBPropType strict_val;
+	RhythmDBPropType fuzzy_val;
+	const RBQueryCreatorPropertyType *property_type;
+} RBQueryCreatorPropertyOption;
+
+extern const RBQueryCreatorPropertyOption property_options[];
+extern const int num_property_options;
+extern const RBQueryCreatorSortOption sort_options[];
+extern const int num_sort_options;
+extern const int DEFAULT_SORTING_COLUMN;
+extern const gint DEFAULT_SORTING_ORDER;
+
+GtkWidget * get_box_widget_at_pos (GtkBox *box, guint pos);
+
+
+#endif /* __RB_QUERY_CREATOR_H */
--- /dev/null	2005-08-18 18:12:49.785368488 +1000
+++ widgets/rb-query-creator-properties.c	2005-08-24 00:38:04.000000000 +1000
@@ -0,0 +1,467 @@
+/*
+ *  arch-tag: Implementation of RhythmDB query creation properties
+ *
+ *  Copyright (C) 2003, 2004 Colin Walters <walters@gnome.org>
+ *  Copyright (C) 2005 James Livingston <walters@gnome.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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 <libgnome/gnome-i18n.h>
+#include <gtk/gtk.h>
+
+#include "rhythmdb.h"
+#include "rb-query-creator-private.h"
+#include "rb-rating.h"
+
+const RBQueryCreatorPropertyType string_property_type;
+const RBQueryCreatorPropertyType escaped_string_property_type;
+const RBQueryCreatorPropertyType rating_property_type;
+const RBQueryCreatorPropertyType integer_property_type;
+const RBQueryCreatorPropertyType duration_property_type;
+const RBQueryCreatorPropertyType relative_time_property_type;
+
+static GtkWidget * stringCriteriaCreateWidget (gboolean *constrain);
+static void stringCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void stringCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static void escapedStringCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void escapedStringCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static GtkWidget * ratingCriteriaCreateWidget (gboolean *constrain);
+static void ratingCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void ratingCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static GtkWidget * integerCriteriaCreateWidget (gboolean *constrain);
+static void integerCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void integerCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static GtkWidget * durationCriteriaCreateWidget (gboolean *constrain);
+static void durationCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void durationCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static GtkWidget * relativeTimeCriteriaCreateWidget (gboolean *constrain);
+static void relativeTimeCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void relativeTimeCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+
+
+/*
+ * This table is the list of properties that are displayed in the query-creator
+ */
+const RBQueryCreatorPropertyOption property_options[] =
+{
+	{ N_("Title"), RHYTHMDB_PROP_TITLE, RHYTHMDB_PROP_TITLE_FOLDED, &string_property_type },
+	{ N_("Artist"), RHYTHMDB_PROP_ARTIST, RHYTHMDB_PROP_ARTIST_FOLDED, &string_property_type },
+	{ N_("Album"), RHYTHMDB_PROP_ALBUM, RHYTHMDB_PROP_ALBUM_FOLDED, &string_property_type },
+	{ N_("Genre"), RHYTHMDB_PROP_GENRE, RHYTHMDB_PROP_GENRE_FOLDED, &string_property_type },
+	{ N_("Rating"), RHYTHMDB_PROP_RATING, RHYTHMDB_PROP_RATING, &rating_property_type },
+	{ N_("Path"), RHYTHMDB_PROP_LOCATION, RHYTHMDB_PROP_LOCATION, &escaped_string_property_type },
+
+	{ N_("Play Count"), RHYTHMDB_PROP_PLAY_COUNT, RHYTHMDB_PROP_PLAY_COUNT, &integer_property_type },
+	{ N_("Track Number"), RHYTHMDB_PROP_TRACK_NUMBER, RHYTHMDB_PROP_TRACK_NUMBER, &integer_property_type },
+	{ N_("Disc Number"), RHYTHMDB_PROP_DISC_NUMBER, RHYTHMDB_PROP_DISC_NUMBER, &integer_property_type },
+	{ N_("Bitrate"), RHYTHMDB_PROP_BITRATE, RHYTHMDB_PROP_BITRATE, &integer_property_type },
+
+	{ N_("Duration"), RHYTHMDB_PROP_DURATION, RHYTHMDB_PROP_DURATION, &duration_property_type },
+
+	{ 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 },
+};
+
+const int num_property_options = G_N_ELEMENTS (property_options);
+
+
+/*
+ * This table describes which properties can be used for sorting a playlist
+ * All entries MUST have column keys column keys listed in rb-entry-view.c
+ */
+const RBQueryCreatorSortOption sort_options[] =
+{
+	{ N_("Artist"), "Artist", N_("In reverse alphabetical order") },
+	{ N_("Album"), "Album", N_("In reverse alphabetical order") },
+	{ N_("Genre"), "Genre", N_("In reverse alphabetical order") },
+	{ N_("Title"), "Title", N_("In reverse alphabetical order") },
+	{ N_("Rating"), "Rating", N_("With more highly rated tracks first") },
+	{ N_("Play Count"), "PlayCount", N_("With more often played songs first") },
+	{ N_("Duration"), "Time", N_("With longer tracks first") },
+	{ N_("Track Number"), "Track", N_("In decreasing order")},
+	{ N_("Last Played"), "LastPlayed", N_("With more recently played tracks first") },
+};
+
+const int num_sort_options = G_N_ELEMENTS (sort_options);
+const int DEFAULT_SORTING_COLUMN = 0;
+const gint DEFAULT_SORTING_ORDER = GTK_SORT_ASCENDING;
+
+
+/*
+ * This is the property type for string properties
+ */
+
+const RBQueryCreatorCriteriaOption string_criteria_options[] =
+{
+	{ N_("contains"), 0, RHYTHMDB_QUERY_PROP_LIKE },
+	{ N_("does not contain"), 0, RHYTHMDB_QUERY_PROP_NOT_LIKE },
+	{ N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS }
+};
+
+const RBQueryCreatorPropertyType string_property_type =
+{
+	G_N_ELEMENTS (string_criteria_options),
+	string_criteria_options,
+	stringCriteriaCreateWidget,
+	stringCriteriaSetWidgetData,
+	stringCriteriaGetWidgetData
+};
+
+const RBQueryCreatorPropertyType escaped_string_property_type =
+{
+	G_N_ELEMENTS (string_criteria_options),
+	string_criteria_options,
+	stringCriteriaCreateWidget,
+	escapedStringCriteriaSetWidgetData,
+	escapedStringCriteriaGetWidgetData
+};
+
+
+/*
+ * This are the property types for numeric quantities, such as rating and playcounts
+ */
+
+const RBQueryCreatorCriteriaOption numeric_criteria_options[] =
+{
+	{ N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS },
+	{ N_("at least"), 1, RHYTHMDB_QUERY_PROP_GREATER },	/* matches if A >= B */
+	{ N_("at most"), 1, RHYTHMDB_QUERY_PROP_LESS }		/* matches if A <= B */
+};
+
+const RBQueryCreatorPropertyType rating_property_type =
+{
+	G_N_ELEMENTS (numeric_criteria_options),
+	numeric_criteria_options,
+	ratingCriteriaCreateWidget,
+	ratingCriteriaSetWidgetData,
+	ratingCriteriaGetWidgetData
+};
+
+const RBQueryCreatorPropertyType integer_property_type =
+{
+	G_N_ELEMENTS (numeric_criteria_options),
+	numeric_criteria_options,
+	integerCriteriaCreateWidget,
+	integerCriteriaSetWidgetData,
+	integerCriteriaGetWidgetData
+};
+
+const RBQueryCreatorPropertyType duration_property_type =
+{
+	G_N_ELEMENTS (numeric_criteria_options),
+	numeric_criteria_options,
+	durationCriteriaCreateWidget,
+	durationCriteriaSetWidgetData,
+	durationCriteriaGetWidgetData
+};
+
+
+/*
+ * This is the property type for relative time properties, such as last played and first seen
+ */
+
+typedef struct
+{
+	const char *name;
+	gulong timeMultiplier;
+} RBQueryCreatorTimeUnitOption;
+
+const RBQueryCreatorCriteriaOption relative_time_criteria_options[] =
+{
+	/*
+	 * Translators: this will match when within <value> of the current time
+	 * e.g. "in the last" "7 days" will match if within 7 days of the current time
+	 */
+	{ N_("in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN },
+	
+	/*
+	 * Translators: this is the opposite of the above, and will match if not
+	 * within <value> of the current time
+	 */
+	{ N_("not in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN }
+};
+
+const RBQueryCreatorPropertyType relative_time_property_type =
+{
+	G_N_ELEMENTS (relative_time_criteria_options),
+	relative_time_criteria_options,
+	relativeTimeCriteriaCreateWidget,
+	relativeTimeCriteriaSetWidgetData,
+	relativeTimeCriteriaGetWidgetData
+};
+
+const RBQueryCreatorTimeUnitOption time_unit_options[] =
+{
+	{ N_("seconds"), 1 },
+	{ N_("minutes"), 60 },
+	{ N_("hours"), 60 * 60 },
+	{ N_("days"), 60 * 60 * 24 },
+	{ N_("weeks"), 60 * 60 * 24 * 7 }
+};
+
+const int time_unit_options_default = 4; /* days */
+
+
+/*
+ * Implementation for the string properties, using a single GtkEntry.
+ */
+
+static GtkWidget *
+stringCriteriaCreateWidget (gboolean *constrain)
+{
+	return gtk_entry_new ();
+}
+
+static void
+stringCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	gtk_entry_set_text (GTK_ENTRY (widget), g_value_get_string (val));
+}
+
+static void
+stringCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	const char* text = gtk_entry_get_text (GTK_ENTRY (widget));
+
+	g_value_init (val, G_TYPE_STRING);
+	g_value_set_string (val, text);
+}
+
+/* escaped string operations, for use with URIs, etc */
+
+static void
+escapedStringCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	char *text = gnome_vfs_unescape_string (g_value_get_string (val), NULL);
+	gtk_entry_set_text (GTK_ENTRY (widget), text);
+	g_free (text);
+}
+
+static void
+escapedStringCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	char *text = gnome_vfs_escape_path_string (gtk_entry_get_text (GTK_ENTRY (widget)));
+
+	g_value_init (val, G_TYPE_STRING);
+	g_value_set_string (val, text);
+}
+
+
+/*
+ * Implementation for the ratings property, using the RbRating widget
+ */
+
+static void
+set_rating_score (RBRating *rating, gdouble score)
+{
+	g_object_set (G_OBJECT (rating), "rating", score, NULL);
+}
+
+static GtkWidget *
+ratingCriteriaCreateWidget (gboolean *constrain)
+{
+	RBRating *rating = rb_rating_new ();
+	g_signal_connect_object (G_OBJECT (rating), "rated",
+				 G_CALLBACK (set_rating_score), NULL, 0);
+	*constrain = FALSE;
+	return GTK_WIDGET (rating);
+}
+
+static void
+ratingCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	g_object_set (G_OBJECT (widget), "rating", g_value_get_double (val), NULL);
+}
+
+static void
+ratingCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	double rating;
+	g_object_get (G_OBJECT (widget), "rating", &rating, NULL);
+
+	g_value_init (val, G_TYPE_DOUBLE);
+	g_value_set_double (val, rating);
+}
+
+
+/*
+ * Implementation for the integer properties, using a single GtkSpinButton.
+ */
+
+static GtkWidget *
+integerCriteriaCreateWidget (gboolean *constrain)
+{
+	return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT, 1.0);
+}
+
+static void
+integerCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	gulong num = g_value_get_ulong (val);
+	g_assert (num <= G_MAXINT);
+		
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), (gint)num );
+}
+
+static void
+integerCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	gint num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+	g_assert (num >= 0);
+
+	g_value_init (val, G_TYPE_ULONG);
+	g_value_set_ulong (val, (gulong)num);
+}
+
+
+/*
+ * Implementation for the duration property, using two single GtkSpinButtons.
+ */
+
+static GtkWidget *
+durationCriteriaCreateWidget (gboolean *constrain)
+{
+	GtkBox *box;
+	GtkWidget *minutesSpin;
+	GtkWidget *minutesLabel;
+	GtkWidget *secondsSpin;
+
+	/* the widget for Duration is set out like the following [ 2] : [30] */
+	box = GTK_BOX (gtk_hbox_new (FALSE, 3));
+	
+	minutesSpin = gtk_spin_button_new_with_range (0.0, G_MAXINT, 1.0);
+	gtk_box_pack_start (box, minutesSpin, FALSE, FALSE, 0);
+	
+	minutesLabel = gtk_label_new (":");
+	gtk_box_pack_start (box, minutesLabel, FALSE, FALSE, 0);
+	
+	secondsSpin = gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
+	gtk_box_pack_start (box, secondsSpin, FALSE, FALSE, 0);
+	
+	gtk_widget_show_all (GTK_WIDGET (box));
+	return GTK_WIDGET (box);
+}
+
+static void
+durationCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	GtkSpinButton *minutesSpinner = GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget), 0));
+	GtkSpinButton *secondsSpinner = GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget), 2));
+	
+	gtk_spin_button_set_value (minutesSpinner, (gdouble) (g_value_get_ulong (val) / 60));
+	gtk_spin_button_set_value (secondsSpinner, (gdouble) (g_value_get_ulong (val) % 60));
+}
+
+static void
+durationCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	
+	GtkSpinButton *minutesSpinner = GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget), 0));
+	GtkSpinButton *secondsSpinner = GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget), 2));
+	
+	gint value = gtk_spin_button_get_value_as_int (minutesSpinner) * 60
+		   + gtk_spin_button_get_value_as_int (secondsSpinner);
+	g_assert (value >= 0);
+
+	g_value_init (val, G_TYPE_ULONG);
+	g_value_set_ulong (val, (gulong) value);
+}
+
+
+/*
+ * Implementation for the relative time properties, using a spin button and a menu.
+ */
+
+static GtkWidget*
+create_time_unit_option_menu (const RBQueryCreatorTimeUnitOption *options,
+			     int length)
+{
+	GtkWidget *menu = gtk_menu_new ();
+	GtkWidget *option_menu = gtk_option_menu_new ();
+	int i;
+
+	for (i = 0; i < length; i++) {
+		GtkWidget *menu_item = gtk_menu_item_new_with_label (_(options[i].name));
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+	}
+
+	gtk_widget_show_all (menu);
+	gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
+  
+	return option_menu;
+}
+
+static GtkWidget *
+relativeTimeCriteriaCreateWidget (gboolean *constrain)
+{
+	GtkBox *box;
+
+	GtkWidget *timeSpin;
+	GtkWidget *timeOption;
+
+	box = GTK_BOX (gtk_hbox_new (FALSE, 6));
+	
+	timeSpin = gtk_spin_button_new_with_range (1.0, G_MAXINT, 1.0);
+	gtk_box_pack_start_defaults (box, timeSpin);
+	
+	timeOption = create_time_unit_option_menu (time_unit_options, G_N_ELEMENTS (time_unit_options));
+	gtk_option_menu_set_history(GTK_OPTION_MENU (timeOption), time_unit_options_default);
+	gtk_box_pack_start_defaults (box, timeOption);
+		
+	gtk_widget_show_all (GTK_WIDGET (box));
+	return GTK_WIDGET (box);
+}
+
+static void
+relativeTimeCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	GtkBox *box = GTK_BOX (widget);
+	
+	GtkSpinButton *timeSpin = GTK_SPIN_BUTTON (get_box_widget_at_pos (box, 0));
+	GtkOptionMenu *unitMenu = GTK_OPTION_MENU (get_box_widget_at_pos (box, 1));
+	
+	gulong time = g_value_get_ulong (val);
+	gulong unit = 0;
+	int i;
+
+	/* determine the best units to use for the given value */
+	for (i = 0; i < G_N_ELEMENTS(time_unit_options); i++) {
+		/* find out if the time is an even multiple of the unit */
+		if (time % time_unit_options[i].timeMultiplier == 0)
+			unit = i;
+	}
+
+	time = time / time_unit_options[unit].timeMultiplier;
+	g_assert (time < G_MAXINT);
+	/* set the time value and unit*/
+	gtk_option_menu_set_history(unitMenu, unit);
+	gtk_spin_button_set_value(timeSpin, time);
+}
+
+static void
+relativeTimeCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	GtkSpinButton *timeSpin = GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget), 0));
+	GtkOptionMenu *unitMenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (widget), 1));
+
+	gulong timeMultiplier = time_unit_options [gtk_option_menu_get_history (unitMenu)].timeMultiplier;
+	gint value = gtk_spin_button_get_value_as_int (timeSpin) * timeMultiplier;
+	g_assert (value >= 0);
+
+	g_value_init (val, G_TYPE_ULONG);
+	g_value_set_ulong (val, (gulong) value);
+}
