--- rhythmdb/rhythmdb-tree.c	10 Jul 2005 12:37:56 -0000	1.66
+++ rhythmdb/rhythmdb-tree.c	27 Jul 2005 13:32:19 -0000
@@ -1228,6 +1228,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:
 		{
--- rhythmdb/rhythmdb.c	14 Jul 2005 23:34:17 -0000	1.101
+++ rhythmdb/rhythmdb.c	27 Jul 2005 13:32:34 -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 *) "]"
@@ -204,7 +206,6 @@
 static void rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
  					  GnomeVFSVolume *volume, 
  					  gpointer data);
-
 enum
 {
 	PROP_0,
@@ -2174,6 +2175,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));
@@ -2244,6 +2247,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;
@@ -2402,6 +2407,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;
 		}		
 	}
 }
@@ -2442,6 +2457,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 ();
 
@@ -2449,7 +2468,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);
@@ -2638,6 +2659,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 iff property1 is within property2 of the current time"),
+			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN, "True iff property1 is not within property2 of the current time"),
 			{ 0, 0, 0 }
 		};
 
--- rhythmdb/rhythmdb.h	5 Jul 2005 09:15:43 -0000	1.41
+++ rhythmdb/rhythmdb.h	27 Jul 2005 13:32:36 -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 */
--- widgets/Makefile.am	12 Jun 2005 18:00:32 -0000	1.17
+++ widgets/Makefile.am	27 Jul 2005 13:32:47 -0000
@@ -43,6 +43,8 @@
 	rb-tree-view.h					\
 	rb-query-creator.c				\
 	rb-query-creator.h				\
+	rb-query-creator-private.h			\
+	rb-query-creator-properties.c			\
 	rb-tree-view-column.c				\
 	rb-tree-view-column.h
 
--- widgets/rb-query-creator.c	25 Jul 2005 16:30:58 -0000	1.29
+++ widgets/rb-query-creator.c	27 Jul 2005 13:32:58 -0000
@@ -27,50 +27,12 @@
 
 #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);
@@ -85,9 +47,6 @@
 					      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,
@@ -96,29 +55,31 @@
 static void setup_property_option_menu (RBQueryCreator *creator,
 					GtkWidget *option_menu,
 					const RBQueryCreatorPropertyOption *options,
-					int length, gboolean activate_first);
+					int length);
 static void setup_criteria_option_menu (GtkWidget *option_menu,
 					const RBQueryCreatorCriteriaOption *options,
-					int length, gboolean activate_first);
+					int length);
 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);
+						int length);
 static GtkWidget * option_menu_get_active_child (GtkWidget *option_menu);
-static GtkWidget * get_box_widget_at_pos (GtkBox *box, guint pos);
 
 static GtkWidget * append_row (RBQueryCreator *dialog);
 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);
 
+static int get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options,
+			    	  int length, RhythmDBPropType prop);
+
 struct RBQueryCreatorPrivate
 {
 	RhythmDB *db;
 
 	gboolean creating;
-	
+
 	GtkSizeGroup *property_size_group;
 	GtkSizeGroup *criteria_size_group;
 	GtkSizeGroup *entry_size_group;
@@ -132,7 +93,6 @@
 	GtkWidget *limit_check;
 	GtkWidget *limit_entry;
 	GtkWidget *limit_option;
-	GPtrArray *queries;
 };
 
 static GObjectClass *parent_class = NULL;
@@ -236,7 +196,10 @@
 	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"));
+	if (dlg->priv->creating)
+		gtk_window_set_title (GTK_WINDOW (dlg), _("Create Automatic Playlist"));
+	else
+		gtk_window_set_title (GTK_WINDOW (dlg), _("Edit Automatic Playlist"));
 
 	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);
 	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dlg)->vbox), 2);
@@ -254,8 +217,7 @@
 
 	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);
+	limit_toggled_cb (dlg->priv->limit_check, dlg);
 
 	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),
@@ -295,7 +257,6 @@
 	g_object_unref (G_OBJECT (dlg->priv->button_size_group));
 
 	g_list_free (dlg->priv->rows);
-	
 	g_free (dlg->priv);
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -381,7 +342,9 @@
 	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,43 +352,41 @@
 		}
 
 		propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (rows->data), 0));
-		
-		select_property_from_value (GTK_WIDGET (propmenu), property_options,
-					    G_N_ELEMENTS (property_options), data->propid);
+		index = get_property_index_from_proptype (property_options, num_property_options, data->propid);
+		gtk_option_menu_set_history (propmenu, index);
 
-		criteria_menu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (rows->data),
-									1));
+		criteria_menu = get_box_widget_at_pos (GTK_BOX (rows->data), 1);
+		select_criteria_from_value (creator, criteria_menu, data->propid, data->type);
 
-		select_criteria_from_value (creator,
-					    GTK_WIDGET (criteria_menu), data->propid,
-					    data->type);
+		property_type = property_options[index].property_type;
+		g_assert (property_type->setWidgetData != NULL);
+		property_type->setWidgetData (get_box_widget_at_pos (GTK_BOX (rows->data), 2),
+					      data->val);
 
-		if (data->propid != RHYTHMDB_PROP_RATING) {
-			GtkEntry *text = GTK_ENTRY (get_box_widget_at_pos (GTK_BOX (rows->data), 2));
-			
-			gtk_entry_set_text (text, g_value_get_string (data->val));
-		} 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);
-		}
 		rows = rows->next;
 	}
 
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (creator->priv->disjunction_check),
-				      disjunction);
+	/* setup the limits */
+	{
+		int limit;
+
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (creator->priv->disjunction_check),
+					      disjunction);
+		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);
+			limit = limit_count;
+		} else if (limit_size % 1000 == 0) {
+			gtk_option_menu_set_history (GTK_OPTION_MENU (creator->priv->limit_option), 2);
+			limit = limit_size / 1000;
+		} else {
+			gtk_option_menu_set_history (GTK_OPTION_MENU (creator->priv->limit_option), 1);
+			limit = limit_size;
+		}
 
-	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);
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (creator->priv->limit_entry), limit);
 	}
 }
 
@@ -442,7 +403,7 @@
 	return GTK_WIDGET (creator);
 }
 
-static GtkWidget *
+GtkWidget *
 get_box_widget_at_pos (GtkBox *box, guint pos)
 {
 	GtkWidget *ret = NULL;
@@ -461,55 +422,20 @@
 	}
 	g_list_free (children);
 	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->createWidget != NULL);
+
+	*constrain = TRUE;
+	return property_type->createWidget (constrain);
 }
 
 static int
@@ -527,17 +453,10 @@
 	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));
-	
 	sub_query = g_ptr_array_new ();
-	
 	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));
@@ -545,62 +464,38 @@
 										       1));
 		guint prop_position = extract_option_menu_index (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_options = prop_option->property_type->criteria_options;
 		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);
+		g_assert (prop_option->property_type->getWidgetData != NULL);
+		{
+			RhythmDBQueryData *data = g_new0 (RhythmDBQueryData, 1);
+			GValue *val = g_new0 (GValue, 1);
 			
-			/* 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_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,
-						       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);
+			data->type = criteria_option->val;
+			data->propid = criteria_option->strict ? prop_option->strict_val : prop_option->fuzzy_val;
+			
+			prop_option->property_type->getWidgetData (get_box_widget_at_pos (GTK_BOX (row->data), 2), val);
+			data->val = val;
+			
+			g_ptr_array_add (sub_query, data);
 		}
+
+		if (disjunction && row->next)
+			rhythmdb_query_append (dlg->priv->db,
+					       sub_query,
+					       RHYTHMDB_QUERY_DISJUNCTION,
+					       RHYTHMDB_QUERY_END);
 	}
-	rhythmdb_query_append (dlg->priv->db,
-			       query,
-			       RHYTHMDB_QUERY_SUBQUERY,
-			       sub_query,
-			       RHYTHMDB_QUERY_END);
+	query = rhythmdb_query_parse (dlg->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);
 	return query;
 }
 
@@ -688,6 +583,7 @@
 	GtkBox *hbox;
 	GList *rows;
 	guint len;
+	gboolean constrain;
 
 	rows = dialog->priv->rows;
 	len = g_list_length (rows);
@@ -698,20 +594,18 @@
 	gtk_box_reorder_child (dialog->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);
+	option = create_property_option_menu (dialog, property_options, num_property_options);
 	gtk_size_group_add_widget (dialog->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);
+	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 (dialog->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 (dialog, property_options[0].strict_val, &constrain);
+	if (constrain)
+		gtk_size_group_add_widget (dialog->priv->entry_size_group, entry);
 	gtk_box_pack_start_defaults (hbox, GTK_WIDGET (entry));
 
 	remove_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
@@ -724,22 +618,20 @@
 	return GTK_WIDGET (hbox);
 }
 
-static void
-select_property_from_value (GtkWidget *option_menu,
-			    const RBQueryCreatorPropertyOption *options,
-			    int length, RhythmDBPropType prop)
+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,8 +642,10 @@
 	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) {
 			gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), i);
@@ -771,19 +665,28 @@
 	const RBQueryCreatorPropertyOption *prop_option;
 	const RBQueryCreatorCriteriaOption *criteria_options;
 	guint length;
+	guint old_value;
 	gboolean constrain;
 
+	prop_option = &property_options[extract_option_menu_index (propmenu)];
+	old_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (propmenu), "prop-menu old-value"));
+
+	/* don't recreate the critera 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 (extract_option_menu_index (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_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (criteria));
@@ -794,6 +697,7 @@
 	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_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (entry));
@@ -805,15 +709,14 @@
 setup_property_option_menu (RBQueryCreator *creator,
 			    GtkWidget *option_menu,
 			    const RBQueryCreatorPropertyOption *options,
-			    int length, gboolean activate_first)
+			    int length)
 {
 	GtkWidget *menu;
 	GtkWidget *menu_item;
 	int i;
 
 	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),
@@ -822,10 +725,11 @@
 		gtk_widget_show (menu_item);
 	}
 
-	if (activate_first)
-		gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), 0);
+	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);
@@ -834,14 +738,13 @@
 static void
 setup_criteria_option_menu (GtkWidget *option_menu,
 			    const RBQueryCreatorCriteriaOption *options,
-			    int length, gboolean activate_first)
+			    int length)
 {
 	GtkWidget *menu;
 	GtkWidget *menu_item;
 	int i;
 
 	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));
@@ -851,43 +754,31 @@
 		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);
+	gtk_widget_show (menu);
 }
 
 static GtkWidget*
 create_property_option_menu (RBQueryCreator *creator,
 			     const RBQueryCreatorPropertyOption *options,
-			     int length, gboolean activate_first)
+			     int length)
 {
-	GtkWidget *menu;
 	GtkWidget *option_menu;
-  
-	menu = gtk_menu_new ();
-	gtk_widget_show (menu);
     
 	option_menu = gtk_option_menu_new ();
-
-	setup_property_option_menu (creator, option_menu, options, length, activate_first);
+	setup_property_option_menu (creator, option_menu, options, length);
   
 	return option_menu;
 }
 
 static GtkWidget*
 create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
-			     int length, gboolean activate_first)
+			     int length)
 {
-	GtkWidget *menu;
 	GtkWidget *option_menu;
-  
-	menu = gtk_menu_new ();
-	gtk_widget_show (menu);
     
 	option_menu = gtk_option_menu_new ();
-
-	setup_criteria_option_menu (option_menu, options, length, activate_first);
+	setup_criteria_option_menu (option_menu, options, length);
   
 	return option_menu;
 }
--- /dev/null	2005-07-27 16:31:12.477604184 +1000
+++ widgets/rb-query-creator-private.h	2005-07-26 02:39:01.000000000 +1000
@@ -0,0 +1,65 @@
+/*
+ *  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 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	createWidget;
+	CriteriaSetWidgetData	setWidgetData;
+	CriteriaGetWidgetData	getWidgetData;
+} 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;
+
+GtkWidget * get_box_widget_at_pos (GtkBox *box, guint pos);
+
+
+#endif /* __RB_QUERY_CREATOR_H */
--- /dev/null	2005-07-27 16:31:12.477604184 +1000
+++ widgets/rb-query-creator-properties.c	2005-07-27 21:44:00.000000000 +1000
@@ -0,0 +1,435 @@
+/*
+ *  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_("Last Played"), RHYTHMDB_PROP_LAST_PLAYED, RHYTHMDB_PROP_LAST_PLAYED, &relative_time_property_type },
+	{ N_("First Seen"), RHYTHMDB_PROP_FIRST_SEEN, RHYTHMDB_PROP_FIRST_SEEN, &relative_time_property_type },
+};
+
+const int num_property_options = G_N_ELEMENTS (property_options);
+
+/*
+ * 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[] =
+{
+	{ N_("in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN },
+	{ 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_take_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);
+}
