Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/rhythmbox/ChangeLog,v
retrieving revision 1.1491
diff -u -r1.1491 ChangeLog
--- ChangeLog	23 Dec 2005 22:03:10 -0000	1.1491
+++ ChangeLog	24 Dec 2005 18:42:40 -0000
@@ -1,3 +1,25 @@
+2005-12-25  Alex Lancaster  <alexl@users.sourceforge.net>
+
+	* rhythmdb/rhythmdb-tree.c (evaluate_conjunctive_subquery): Add
+	support for RHYTHMDB_QUERY_PROP_YEAR_{EQUALS,GREATER,LESS}
+	database search for year in date.  This handles dates that include
+	day and month, not just year.
+
+	* rhythmdb/rhythmdb.h (enum):
+
+	* rhythmdb/rhythmdb.c (RB_PARSE_YEAR_EQUALS)
+	(rhythmdb_query_parse_valist, rhythmdb_query_free)
+	(rhythmdb_query_serialize, rhythmdb_query_deserialize)
+	(rhythmdb_query_get_type): Support special
+	RHYTHMDB_QUERY_PROP_YEAR_{EQUALS,GREATER,LESS} database search for
+	year in date.
+
+	* widgets/rb-query-creator-properties.c:
+	(property_options, year_property_type)
+	(yearCriteriaSetWidgetData, yearCriteriaGetWidgetData): New widget
+	that supports automatic playlists including year as a criteria.
+	Performs the internal Julian to year conversion.  Closes #321341.
+
 2005-12-24  Jonathan Matthew  <jonathan@kaolin.hn.org>
 
 	* shell/main.c: (load_uri_args): don't compile this in if
Index: rhythmdb/rhythmdb-tree.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb-tree.c,v
retrieving revision 1.85
diff -u -r1.85 rhythmdb-tree.c
--- rhythmdb/rhythmdb-tree.c	19 Dec 2005 08:05:15 -0000	1.85
+++ rhythmdb/rhythmdb-tree.c	24 Dec 2005 18:42:47 -0000
@@ -1478,6 +1478,56 @@
 		case RHYTHMDB_QUERY_PROP_LESS:
 			RHYTHMDB_PROPERTY_COMPARE (>)
 			break;
+		case RHYTHMDB_QUERY_PROP_YEAR_EQUALS:
+		case RHYTHMDB_QUERY_PROP_YEAR_LESS:
+		case RHYTHMDB_QUERY_PROP_YEAR_GREATER:
+		{
+			GDate *date = NULL;	
+			GDate *date_begin = NULL;	
+			GDate *date_end = NULL;		
+			gulong curr_song_date;		
+			gulong search_date;		
+			gulong begin;			
+			gulong end;			
+			gint year;					
+
+			g_assert (rhythmdb_get_property_type (db, data->propid) == G_TYPE_ULONG); 
+			curr_song_date = rhythmdb_entry_get_ulong (entry, data->propid); 
+			search_date = g_value_get_ulong (data->val);	
+
+			/* get year for search date */
+			date = g_date_new();				
+			g_date_set_julian(date, search_date);		
+			year = (gint)g_date_get_year(date);	
+	
+			/* get Julian dates for beginning and end of year */ 
+			date_begin = g_date_new_dmy(1, G_DATE_JANUARY, year); 
+			begin = (gulong)g_date_get_julian(date_begin);	
+			date_end = g_date_new_dmy(31, G_DATE_DECEMBER, year); 
+			end =  (gulong)g_date_get_julian(date_end);	
+
+			g_date_free(date);				
+			g_date_free(date_begin);			
+			g_date_free(date_end);				
+
+			switch (data->type) {
+			case RHYTHMDB_QUERY_PROP_YEAR_EQUALS:
+				if (!((curr_song_date >= begin) && 
+				      (curr_song_date <= end)))
+					return FALSE;
+				break;
+			case RHYTHMDB_QUERY_PROP_YEAR_LESS:
+				if (!(curr_song_date < begin))
+					return FALSE;
+				break;
+			case RHYTHMDB_QUERY_PROP_YEAR_GREATER:
+				if (!(curr_song_date > end))
+					return FALSE;
+				break;
+			}
+
+			break;
+		}
 		case RHYTHMDB_QUERY_END:
 		case RHYTHMDB_QUERY_DISJUNCTION:
 			g_assert_not_reached ();
Index: rhythmdb/rhythmdb.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb.c,v
retrieving revision 1.152
diff -u -r1.152 rhythmdb.c
--- rhythmdb/rhythmdb.c	19 Dec 2005 10:17:12 -0000	1.152
+++ rhythmdb/rhythmdb.c	24 Dec 2005 18:42:53 -0000
@@ -61,6 +61,9 @@
 #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_YEAR_EQUALS RB_PARSE_EQUALS
+#define RB_PARSE_YEAR_GREATER RB_PARSE_GREATER
+#define RB_PARSE_YEAR_LESS RB_PARSE_LESS
 
 #define RB_PARSE_NICK_START (xmlChar *) "["
 #define RB_PARSE_NICK_END (xmlChar *) "]"
@@ -2724,6 +2727,9 @@
 		case RHYTHMDB_QUERY_PROP_LESS:
 		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
 		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
+		case RHYTHMDB_QUERY_PROP_YEAR_EQUALS:
+		case RHYTHMDB_QUERY_PROP_YEAR_GREATER:
+		case RHYTHMDB_QUERY_PROP_YEAR_LESS:  
 			data->propid = va_arg (args, guint);
 			data->val = g_new0 (GValue, 1);
 			g_value_init (data->val, rhythmdb_get_property_type (db, data->propid));
@@ -2853,6 +2859,9 @@
 		case RHYTHMDB_QUERY_PROP_LESS:
 		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
 		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
+		case RHYTHMDB_QUERY_PROP_YEAR_EQUALS:
+		case RHYTHMDB_QUERY_PROP_YEAR_GREATER:
+		case RHYTHMDB_QUERY_PROP_YEAR_LESS:  
 			g_value_unset (data->val);
 			g_free (data->val);
 			break;
@@ -2995,6 +3004,11 @@
 			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_YEAR_EQUALS:
+			subnode = xmlNewChild (node, NULL, RB_PARSE_YEAR_EQUALS, 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_DISJUNCTION:
 			subnode = xmlNewChild (node, NULL, RB_PARSE_DISJ, NULL);
 			break;
@@ -3005,11 +3019,21 @@
 			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_YEAR_GREATER:
+			subnode = xmlNewChild (node, NULL, RB_PARSE_YEAR_GREATER, 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_LESS:
 			subnode = xmlNewChild (node, NULL, RB_PARSE_LESS, 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_YEAR_LESS:  
+			subnode = xmlNewChild (node, NULL, RB_PARSE_YEAR_LESS, 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_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));
@@ -3055,11 +3079,23 @@
 		} else if (!xmlStrcmp (child->name, RB_PARSE_NOT_LIKE)) {
 			data->type = RHYTHMDB_QUERY_PROP_NOT_LIKE;
 		} else if (!xmlStrcmp (child->name, RB_PARSE_EQUALS)) {
-			data->type = RHYTHMDB_QUERY_PROP_EQUALS;
+			if (!xmlStrcmp(xmlGetProp(child, RB_PARSE_PROP), 
+				       (xmlChar *)"date")) 
+				data->type = RHYTHMDB_QUERY_PROP_YEAR_EQUALS;
+			else 
+				data->type = RHYTHMDB_QUERY_PROP_EQUALS;
 		} else if (!xmlStrcmp (child->name, RB_PARSE_GREATER)) {
-			data->type = RHYTHMDB_QUERY_PROP_GREATER;
+			if (!xmlStrcmp(xmlGetProp(child, RB_PARSE_PROP), 
+				       (xmlChar *)"date")) 
+				data->type = RHYTHMDB_QUERY_PROP_YEAR_GREATER;
+			else 
+				data->type = RHYTHMDB_QUERY_PROP_GREATER;
 		} else if (!xmlStrcmp (child->name, RB_PARSE_LESS)) {
-			data->type = RHYTHMDB_QUERY_PROP_LESS;
+			if (!xmlStrcmp(xmlGetProp(child, RB_PARSE_PROP), 
+				       (xmlChar *)"date")) 
+				data->type = RHYTHMDB_QUERY_PROP_YEAR_LESS;
+			else 
+				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)) {
@@ -3072,6 +3108,9 @@
 		    || !xmlStrcmp (child->name, RB_PARSE_EQUALS)
 		    || !xmlStrcmp (child->name, RB_PARSE_GREATER)
 		    || !xmlStrcmp (child->name, RB_PARSE_LESS)
+		    || !xmlStrcmp (child->name, RB_PARSE_YEAR_EQUALS)
+		    || !xmlStrcmp (child->name, RB_PARSE_YEAR_GREATER)
+		    || !xmlStrcmp (child->name, RB_PARSE_YEAR_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);
@@ -3293,6 +3332,9 @@
 			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"),
+			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_YEAR_EQUALS, "Year equivalence: true if date within year"),
+			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_YEAR_GREATER, "True if date greater than year"),
+			ENUM_ENTRY (RHYTHMDB_QUERY_PROP_YEAR_LESS, "True if date less than year"),
 			{ 0, 0, 0 }
 		};
 
Index: rhythmdb/rhythmdb.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/rhythmdb/rhythmdb.h,v
retrieving revision 1.62
diff -u -r1.62 rhythmdb.h
--- rhythmdb/rhythmdb.h	9 Dec 2005 15:54:49 -0000	1.62
+++ rhythmdb/rhythmdb.h	24 Dec 2005 18:42:54 -0000
@@ -62,6 +62,9 @@
 	RHYTHMDB_QUERY_PROP_LESS,
 	RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN,
 	RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN,
+	RHYTHMDB_QUERY_PROP_YEAR_EQUALS,
+	RHYTHMDB_QUERY_PROP_YEAR_GREATER,   
+	RHYTHMDB_QUERY_PROP_YEAR_LESS,   
 } RhythmDBQueryType;
 
 /* If you modify this enum, don't forget to modify rhythmdb_prop_get_type */
Index: widgets/rb-query-creator-properties.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/widgets/rb-query-creator-properties.c,v
retrieving revision 1.4
diff -u -r1.4 rb-query-creator-properties.c
--- widgets/rb-query-creator-properties.c	5 Nov 2005 05:03:19 -0000	1.4
+++ widgets/rb-query-creator-properties.c	24 Dec 2005 18:42:58 -0000
@@ -31,6 +31,7 @@
 const RBQueryCreatorPropertyType escaped_string_property_type;
 const RBQueryCreatorPropertyType rating_property_type;
 const RBQueryCreatorPropertyType integer_property_type;
+const RBQueryCreatorPropertyType year_property_type;
 const RBQueryCreatorPropertyType duration_property_type;
 const RBQueryCreatorPropertyType relative_time_property_type;
 
@@ -45,6 +46,9 @@
 static GtkWidget * integerCriteriaCreateWidget (gboolean *constrain);
 static void integerCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
 static void integerCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static GtkWidget * yearCriteriaCreateWidget (gboolean *constrain);
+static void yearCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void yearCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
 static GtkWidget * durationCriteriaCreateWidget (gboolean *constrain);
 static void durationCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
 static void durationCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
@@ -62,6 +66,7 @@
 	{ 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_("Year"), RHYTHMDB_PROP_DATE, RHYTHMDB_PROP_DATE, &year_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 },
 
@@ -144,6 +149,20 @@
 	{ N_("at most"), 1, RHYTHMDB_QUERY_PROP_LESS }		/* matches if A <= B */
 };
 
+/*
+ * Property type for date quantities 
+ */
+
+const RBQueryCreatorCriteriaOption year_criteria_options[] =
+{
+	{ N_("in"), 1, RHYTHMDB_QUERY_PROP_YEAR_EQUALS }, 
+	/* matches if within 1-JAN-YEAR to 31-DEC-YEAR */
+	{ N_("after"), 1, RHYTHMDB_QUERY_PROP_YEAR_GREATER },	
+	/* matches if >= 31-DEC-YEAR */
+	{ N_("before"), 1, RHYTHMDB_QUERY_PROP_YEAR_LESS }		
+	/* matches if < 1-DEC-YEAR */
+};
+
 const RBQueryCreatorPropertyType rating_property_type =
 {
 	G_N_ELEMENTS (numeric_criteria_options),
@@ -162,6 +181,15 @@
 	integerCriteriaGetWidgetData
 };
 
+const RBQueryCreatorPropertyType year_property_type =
+{
+	G_N_ELEMENTS (year_criteria_options),
+	year_criteria_options,
+	yearCriteriaCreateWidget,
+	yearCriteriaSetWidgetData,
+	yearCriteriaGetWidgetData
+};
+
 const RBQueryCreatorPropertyType duration_property_type =
 {
 	G_N_ELEMENTS (numeric_criteria_options),
@@ -328,6 +356,45 @@
 	g_value_init (val, G_TYPE_ULONG);
 	g_value_set_ulong (val, (gulong)num);
 }
+
+/* Implementation for Year properties, using a single GtkSpinButton. */
+
+static GtkWidget *
+yearCriteriaCreateWidget (gboolean *constrain)
+{
+	return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT, 1.0);
+}
+
+static void
+yearCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+	GDate *date = NULL;
+	gulong num = g_value_get_ulong (val);
+	g_assert (num <= G_MAXINT);
+
+	/* Create a date structure to get year from */
+	date = g_date_new();
+	g_date_set_julian (date, num);
+	
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), (gint)g_date_get_year(date));
+	g_date_free(date);
+}
+
+static void
+yearCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+	GDate *date = NULL;
+	gint num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+	g_assert (num >=  0);
+
+	/* New date structure, use year set in widget */
+	date = g_date_new_dmy (1, G_DATE_JANUARY, num);
+
+	g_value_init (val, G_TYPE_ULONG);
+	g_value_set_ulong (val, (gulong) g_date_get_julian (date) );
+	g_date_free(date);
+}
+
 
 
 /*
