banshee r3531 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Query src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.ThickClient/Banshee.Query.Gui src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio src/Libraries/Hyena src/Libraries/Hyena.Gui src/Libraries/Hyena.Gui/Hyena.Query.Gui src/Libraries/Hyena/Hyena.Query



Author: gburt
Date: Tue Mar 25 03:57:07 2008
New Revision: 3531
URL: http://svn.gnome.org/viewvc/banshee?rev=3531&view=rev

Log:
2008-03-24  Gabriel Burt  <gabriel burt gmail com>

	This patch fixes issues with smart playlist/search date fields not working
	right/at all.  Also adds support for different widgets within one field on
	the smart playlist editor dialog (eg absolute dates vs relative dates for
	date fields).

	* src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs: Use new
	TimeSpanQueryValue for DurationField, and use RelativeTimeSpanQueryValue
	in addition to DateQueryValue for LastPlayed/Skipped.

	* src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs: Use new
	RelativeTimeSpanQueryValue class.

	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs:
	Avoid crashing if there are no smart playlists.

	* src/Libraries/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs: Old code
	moved mostly to *TimeSpanQueryValueEntry, replaced with an actual abs date
	entry.

	* src/Libraries/Hyena.Gui/Hyena.Query.Gui/NullQueryValueEntry.cs: Empty,
	if the user has selected this 'operator', no more UI is needed.

	* src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs: Handle
	QueryFields having multiple ValueTypes (each with their own set of
	operators).  This allows us to have both relative dates and absolute date
	widegts for one field (LastPlayed, say).

	* src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs: When creating
	a new row, set it to the previous row's field/operator.

	* src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs: Connect new
	QueryValue types to their Entry types.  Add static lookup method.

	* src/Libraries/Hyena/Hyena.Query/DateQueryValue.cs: Move the
	relative/TimeSpan code out.

	* src/Libraries/Hyena.Gui/Makefile.am:
	* src/Libraries/Hyena/Hyena.Query/NullQueryValue.cs: New QueryValue class with
	one operator to see if a value is null.

	* src/Libraries/Hyena.Gui/Hyena.Query.Gui/RelativeTimeSpanQueryValueEntry.cs:
	* src/Libraries/Hyena.Gui/Hyena.Query.Gui/TimeSpanQueryValueEntry.cs: New
	classes, mostly a copy of the old DateQueryValueEntry code, with an entry
	and a units (factors) dropdown (and an "ago" label for the Relative one).

	* src/Libraries/Hyena/Hyena.Query/QueryField.cs: Instead of QueryFields
	having one ValueType, make it an array.

	* src/Libraries/Hyena/Hyena.Query/QueryTermNode.cs:
	* src/Libraries/Hyena/Hyena.Query/QueryValue.cs: Handle multiple ValueTypes.

	* src/Libraries/Hyena/Hyena.Query/QueryOperator.cs: If the Label is null
	return the Name.  Useful for debugging with adding new operators.

	* src/Libraries/Hyena/Makefile.am:
	* src/Libraries/Hyena/Hyena.Query/TimeSpanQueryValue.cs:
	* src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs: New files
	for representing time spans in queries.

	* src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs:
	Use new ExtensionCacheRoot.


Added:
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/NullQueryValueEntry.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/RelativeTimeSpanQueryValueEntry.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/TimeSpanQueryValueEntry.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/NullQueryValue.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/TimeSpanQueryValue.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs
   trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/DateQueryValue.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryField.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOperator.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryTermNode.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryValue.cs
   trunk/banshee/src/Libraries/Hyena/Makefile.am

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs	Tue Mar 25 03:57:07 2008
@@ -67,8 +67,8 @@
 
         public static QueryLimit [] Limits = new QueryLimit [] {
             new QueryLimit ("songs",   Catalog.GetString ("items"), true),
-            new QueryLimit ("minutes", Catalog.GetString ("minutes"), "CoreTracks.Duration", (int) RelativeDateFactor.Minute),
-            new QueryLimit ("hours",   Catalog.GetString ("hours"), "CoreTracks.Duration", (int) RelativeDateFactor.Hour),
+            new QueryLimit ("minutes", Catalog.GetString ("minutes"), "CoreTracks.Duration", (int) TimeFactor.Minute),
+            new QueryLimit ("hours",   Catalog.GetString ("hours"), "CoreTracks.Duration", (int) TimeFactor.Hour),
             new QueryLimit ("MB",      Catalog.GetString ("MB"), "CoreTracks.FileSize", (int) FileSizeFactor.MB),
             new QueryLimit ("GB",      Catalog.GetString ("GB"), "CoreTracks.FileSize", (int) FileSizeFactor.GB)
         };
@@ -121,7 +121,7 @@
         );
 
         public static QueryField RatingField = new QueryField (
-            "rating", Catalog.GetString ("Rating"), "CoreTracks.Rating", typeof(RatingQueryValue),
+            "rating", Catalog.GetString ("Rating"), "CoreTracks.Rating", new Type [] {typeof(RatingQueryValue)},//, typeof(NullQueryValue)},
             // Translators: These are unique search fields.  Please, no spaces. Blank ok.
             Catalog.GetString ("rating"), Catalog.GetString ("stars"),
             "rating", "stars"
@@ -156,7 +156,7 @@
         );
 
         public static QueryField DurationField = new QueryField (
-            "duration", Catalog.GetString ("Duration"), "CoreTracks.Duration", typeof(IntegerQueryValue),
+            "duration", Catalog.GetString ("Duration"), "CoreTracks.Duration", typeof(TimeSpanQueryValue),
             // Translators: These are unique search fields.  Please, no spaces. Blank ok.
             Catalog.GetString ("duration"), Catalog.GetString ("length"), Catalog.GetString ("time"),
             "duration", "length", "time"
@@ -170,21 +170,21 @@
         );
 
         public static QueryField LastPlayedField = new QueryField (
-            "lastplayed", Catalog.GetString ("Last Played Date"), "CoreTracks.LastPlayedStamp", typeof(DateQueryValue),
+            "lastplayed", Catalog.GetString ("Last Played Date"), "CoreTracks.LastPlayedStamp", new Type [] {typeof(RelativeTimeSpanQueryValue), typeof(DateQueryValue)},
             // Translators: These are unique search fields.  Please, no spaces. Blank ok.
             Catalog.GetString ("lastplayed"), Catalog.GetString ("played"), Catalog.GetString ("playedon"),
             "lastplayed", "played", "playedon"
         );
 
         public static QueryField LastSkippedField = new QueryField (
-            "lastskipped", Catalog.GetString ("Last Skipped Date"), "CoreTracks.LastSkippedStamp", typeof(DateQueryValue),
+            "lastskipped", Catalog.GetString ("Last Skipped Date"), "CoreTracks.LastSkippedStamp", new Type [] {typeof(RelativeTimeSpanQueryValue), typeof(DateQueryValue)},
             // Translators: These are unique search fields.  Please, no spaces. Blank ok.
             Catalog.GetString ("lastskipped"), Catalog.GetString ("skipped"), Catalog.GetString ("skippedon"),
             "lastskipped", "skipped", "skippedon"
         );
 
         public static QueryField DateAddedField = new QueryField (
-            "added", Catalog.GetString ("Date Added"), "CoreTracks.DateAddedStamp", typeof(DateQueryValue),
+            "added", Catalog.GetString ("Date Added"), "CoreTracks.DateAddedStamp", new Type [] {typeof(RelativeTimeSpanQueryValue), typeof(DateQueryValue)},
             // Translators: These are unique search fields.  Please, no spaces. Blank ok.
             Catalog.GetString ("added"), Catalog.GetString ("imported"), Catalog.GetString ("addedon"), Catalog.GetString ("dateadded"), Catalog.GetString ("importedon"),
             "added", "imported", "addedon", "dateadded", "importedon"
@@ -319,12 +319,17 @@
             IntegerQueryValue.LessThan.Label         = Catalog.GetString ("less than");
             IntegerQueryValue.GreaterThan.Label      = Catalog.GetString ("more than");
 
-            DateQueryValue.Equal.Label               = Catalog.GetString ("is");
-            DateQueryValue.NotEqual.Label            = Catalog.GetString ("is not");
-            DateQueryValue.LessThanEqual.Label       = Catalog.GetString ("at most");
-            DateQueryValue.GreaterThanEqual.Label    = Catalog.GetString ("at least");
-            DateQueryValue.LessThan.Label            = Catalog.GetString ("less than");
-            DateQueryValue.GreaterThan.Label         = Catalog.GetString ("more than");
+            //DateQueryValue.Equal.Label               = Catalog.GetString ("is");
+            //DateQueryValue.NotEqual.Label            = Catalog.GetString ("is not");
+            //DateQueryValue.LessThanEqual.Label       = Catalog.GetString ("at most");
+            //DateQueryValue.GreaterThanEqual.Label    = Catalog.GetString ("at least");
+            DateQueryValue.LessThan.Label            = Catalog.GetString ("before");
+            DateQueryValue.GreaterThan.Label         = Catalog.GetString ("after");
+
+            RelativeTimeSpanQueryValue.GreaterThan.Label         = Catalog.GetString ("more than");
+            RelativeTimeSpanQueryValue.LessThan.Label            = Catalog.GetString ("less than");
+            RelativeTimeSpanQueryValue.GreaterThanEqual.Label    = Catalog.GetString ("at least");
+            RelativeTimeSpanQueryValue.LessThanEqual.Label       = Catalog.GetString ("at most");
 
             StringQueryValue.Equal.Label             = Catalog.GetString ("is");
             StringQueryValue.NotEqual.Label          = Catalog.GetString ("is not");

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs	Tue Mar 25 03:57:07 2008
@@ -234,13 +234,13 @@
             new_op = new_op.Replace ('<', '>');
             new_op = new_op.Replace ('^', '<');
 
-            DateQueryValue date_value = new DateQueryValue ();
+            RelativeTimeSpanQueryValue date_value = new RelativeTimeSpanQueryValue ();
 
             // Have to flip the operator b/c of how we used to construct the SQL query
             term.Operator = date_value.OperatorSet [new_op];
 
             // Have to negate the value b/c of how we used to constuct the SQL query
-            date_value.SetRelativeValue (-Convert.ToInt64 (val), RelativeDateFactor.Second);
+            date_value.SetRelativeValue (Convert.ToInt64 (val), TimeFactor.Second);
             term.Value = date_value;
         }
 

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs	Tue Mar 25 03:57:07 2008
@@ -83,7 +83,11 @@
                         combo.Active = playlist_id_combo_map [(int)query_value.IntValue];
                     } catch {}
                 }
-                query_value.SetValue (combo_playlist_id_map [combo.Active]);
+
+                if (combo_playlist_id_map.ContainsKey (combo.Active)) {
+                    query_value.SetValue (combo_playlist_id_map [combo.Active]);
+                }
+
                 combo.Changed += HandleValueChanged;
             }
         }

Modified: trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs	Tue Mar 25 03:57:07 2008
@@ -29,7 +29,7 @@
 
         static LastfmSourceContents () {
             DataCore.UserAgent = Banshee.Web.Browser.UserAgent;
-            DataCore.CachePath = System.IO.Path.Combine (Banshee.Base.Paths.ApplicationData, "cache");
+            DataCore.CachePath = System.IO.Path.Combine (Banshee.Base.Paths.ExtensionCacheRoot, "lastfm");
         }
 
         // "Coming Soon: Profile, Friends, Recently Loved Songs, etc")

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs	Tue Mar 25 03:57:07 2008
@@ -37,57 +37,61 @@
 {
     public class DateQueryValueEntry : QueryValueEntry
     {
-        protected SpinButton spin_button;
-        protected ComboBox combo;
         protected DateQueryValue query_value;
 
-        protected static readonly RelativeDateFactor [] factors = new RelativeDateFactor [] {
-            RelativeDateFactor.Second, RelativeDateFactor.Minute, RelativeDateFactor.Hour, RelativeDateFactor.Day,
-            RelativeDateFactor.Week, RelativeDateFactor.Month, RelativeDateFactor.Year
-        };
+        protected SpinButton year_entry = new SpinButton (Double.MinValue, Double.MaxValue, 1.0);
+        protected SpinButton month_entry = new SpinButton (0.0, 12.0, 1.0);
+        protected SpinButton day_entry = new SpinButton (0.0, 31.0, 1.0);
 
-        // Relative: [<|>] [num] [minutes|hours] ago
-        // TODO: Absolute: [>|>=|=|<|<=] [date/time]
         public DateQueryValueEntry () : base ()
         {
-            spin_button = new SpinButton (0.0, 1.0, 1.0);
-            spin_button.Digits = 0;
-            spin_button.WidthChars = 4;
-            spin_button.SetRange (0.0, Double.MaxValue);
-            Add (spin_button);
-
-            combo = ComboBox.NewText ();
-            combo.AppendText (Catalog.GetString ("seconds"));
-            combo.AppendText (Catalog.GetString ("minutes"));
-            combo.AppendText (Catalog.GetString ("hours"));
-            combo.AppendText (Catalog.GetString ("days"));
-            combo.AppendText (Catalog.GetString ("weeks"));
-            combo.AppendText (Catalog.GetString ("months"));
-            combo.AppendText (Catalog.GetString ("years"));
-            combo.Realized += delegate { combo.Active = 1; };
-            Add (combo);
-
-            Add (new Label ("ago"));
-
-            spin_button.ValueChanged += HandleValueChanged;
-            combo.Changed += HandleValueChanged;
+            year_entry.MaxLength = year_entry.WidthChars = 4;
+            month_entry.MaxLength = month_entry.WidthChars = 2;
+            day_entry.MaxLength = day_entry.WidthChars = 2;
+            year_entry.Numeric = month_entry.Numeric = day_entry.Numeric = true;
+            month_entry.Wrap = day_entry.Wrap = true;
+
+            year_entry.Value = (double) DateTime.Now.Year;
+            month_entry.Value = (double) DateTime.Now.Month;
+            day_entry.Value = (double) DateTime.Now.Day;
+
+            year_entry.Changed += HandleValueChanged;
+            month_entry.Changed += HandleValueChanged;
+            day_entry.Changed += HandleValueChanged;
+
+            Add (year_entry);
+            Add (new Label ("-"));
+            Add (month_entry);
+            Add (new Label ("-"));
+            Add (day_entry);
         }
 
         public override QueryValue QueryValue {
             get { return query_value; }
-            set { 
-                spin_button.ValueChanged -= HandleValueChanged;
-                combo.Changed -= HandleValueChanged;
+            set {
+                year_entry.Changed -= HandleValueChanged;
+                month_entry.Changed -= HandleValueChanged;
+                day_entry.Changed -= HandleValueChanged;
+
                 query_value = value as DateQueryValue;
-                combo.Active = Array.IndexOf (factors, query_value.Factor);
-                spin_button.ValueChanged += HandleValueChanged;
-                combo.Changed += HandleValueChanged;
+                year_entry.Value = (double) query_value.DateTime.Year;
+                month_entry.Value = (double) query_value.DateTime.Month;
+                day_entry.Value = (double) query_value.DateTime.Day;
+
+                year_entry.Changed += HandleValueChanged;
+                month_entry.Changed += HandleValueChanged;
+                day_entry.Changed += HandleValueChanged;
             }
         }
 
         protected void HandleValueChanged (object o, EventArgs args)
         {
-            query_value.SetRelativeValue (-spin_button.ValueAsInt, factors [combo.Active]);
+            try {
+                DateTime dt = new DateTime (year_entry.ValueAsInt, month_entry.ValueAsInt, day_entry.ValueAsInt);
+                query_value.SetValue (dt);
+            } catch {
+                Log.Debug ("Caught exception raised because of invalid date");
+            }
         }
     }
 }

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/NullQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/NullQueryValueEntry.cs	Tue Mar 25 03:57:07 2008
@@ -0,0 +1,49 @@
+//
+// NullQueryValueEntry.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+using Hyena.Query;
+using Gtk;
+
+namespace Hyena.Query.Gui
+{
+    public class NullQueryValueEntry : QueryValueEntry
+    {
+        protected NullQueryValue query_value;
+
+        public NullQueryValueEntry () : base ()
+        {
+        }
+
+        public override QueryValue QueryValue {
+            get { return query_value; }
+            set { query_value = value as NullQueryValue; }
+        }
+    }
+}

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs	Tue Mar 25 03:57:07 2008
@@ -29,6 +29,7 @@
 
 using System;
 using System.Text;
+using System.Collections.Generic;
 
 using Gtk;
 using Hyena;
@@ -45,7 +46,10 @@
         public event EventHandler RemoveRequest;
 
         private QueryField field;
-        private QueryValueEntry value_entry;
+        private List<QueryValueEntry> value_entries = new List<QueryValueEntry> ();
+        private List<Operator> operators = new List<Operator> ();
+        private Dictionary<Operator, QueryValueEntry> operator_entries = new Dictionary<Operator, QueryValueEntry> ();
+        private QueryValueEntry current_value_entry;
         private Operator op;
 
         private QueryField [] sorted_fields;
@@ -82,6 +86,7 @@
             field_chooser.Changed += HandleFieldChanged;
             
             op_chooser = ComboBox.NewText ();
+            op_chooser.RowSeparatorFunc = IsRowSeparator;
             op_chooser.Changed += HandleOperatorChanged;
             
             value_box = new HBox ();
@@ -106,6 +111,11 @@
             field_chooser.Active = 0;
         }
 
+        private bool IsRowSeparator (TreeModel model, TreeIter iter)
+        {
+            return String.IsNullOrEmpty (model.GetValue (iter, 0) as string);
+        }
+
         public void Show ()
         {
             field_chooser.ShowAll ();
@@ -115,12 +125,25 @@
         }
         
         private bool first = true;
+        private void SetValueEntry (QueryValueEntry entry)
+        {
+            if (first) {
+                first = false;
+            } else {
+                value_box.Remove (value_box.Children [0]);
+            }
+
+            current_value_entry = entry;
+            value_box.PackStart (current_value_entry, false, true, 0);
+            current_value_entry.ShowAll ();
+        }
+
         private void HandleFieldChanged (object o, EventArgs args)
         {
             QueryField field = sorted_fields [field_chooser.Active];
 
             // Leave everything as is unless the new field is a different type
-            if (this.field != null && (field == this.field || field.ValueType == this.field.ValueType)) {
+            if (this.field != null && (field.ValueTypes.Length == 1 && this.field.ValueTypes.Length == 1 && field.ValueTypes[0] == this.field.ValueTypes[0])) {
                 this.field = field;
                 return;
             }
@@ -129,26 +152,34 @@
 
             this.field = field;
 
-            if (first) {
-                first = false;
-            } else {
-                value_box.Remove (value_box.Children [0]);
-            }
-
-            value_entry = QueryValueEntry.Create (this.field.CreateQueryValue ());
-            value_box.PackStart (value_entry, false, true, 0);
-            value_entry.ShowAll ();
-
             // Remove old type's operators
             while (op_chooser.Model.IterNChildren () > 0) {
                 op_chooser.RemoveText (0);
             }
 
-            // Add new type's operators
-            foreach (Operator op in value_entry.QueryValue.OperatorSet) {
-                op_chooser.AppendText (op.Label);
+            // Add new field's operators
+            int val_count = 0;
+            value_entries.Clear ();
+            operators.Clear ();
+            operator_entries.Clear ();
+            foreach (QueryValue val in this.field.CreateQueryValues ()) {
+                QueryValueEntry entry = QueryValueEntry.Create (val);
+                value_entries.Add (entry);
+
+                if (val_count++ > 0) {
+                    op_chooser.AppendText (String.Empty);
+                    operators.Add (null);
+                }
+
+                foreach (Operator op in val.OperatorSet) {
+                    op_chooser.AppendText (op.Label);
+                    operators.Add (op);
+                    operator_entries [op] = entry;
+                }
             }
-            
+
+            SetValueEntry (value_entries[0]);
+
             // TODO: If we have the same operator that was previously selected, select it
             op_chooser.Changed += HandleOperatorChanged;
             op_chooser.Active = 0;
@@ -156,7 +187,14 @@
         
         private void HandleOperatorChanged (object o, EventArgs args)
         {
-            this.op = value_entry.QueryValue.OperatorSet.Objects [op_chooser.Active];
+            if (op_chooser.Active < 0 || op_chooser.Active >= operators.Count) {
+                return;
+            }
+
+            this.op = operators [op_chooser.Active];
+            if (operator_entries [this.op] != current_value_entry) {
+                SetValueEntry (operator_entries [this.op]);
+            }
 
             //value_entry = new QueryValueEntry <field.ValueType> ();
         }
@@ -185,17 +223,26 @@
                 QueryTermNode node = new QueryTermNode ();
                 node.Field = field;
                 node.Operator = op;
-                node.Value = value_entry.QueryValue;
+                node.Value = current_value_entry.QueryValue;
                 return node;
             }
+
             set {
                 if (value == null) {
                     return;
                 }
 
                 field_chooser.Active = Array.IndexOf (sorted_fields, value.Field);
-                value_entry.QueryValue = value.Value;
-                op_chooser.Active = Array.IndexOf (value.Value.OperatorSet.Objects, value.Operator);
+
+                foreach (QueryValueEntry entry in value_entries) {
+                    if (QueryValueEntry.GetValueType (entry) == value.Value.GetType ()) {
+                        entry.QueryValue = value.Value;
+                        SetValueEntry (entry);
+                        break;
+                    }
+                }
+
+                op_chooser.Active = operators.IndexOf (value.Operator);
             }
         }
     }

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs	Tue Mar 25 03:57:07 2008
@@ -113,6 +113,11 @@
             entry_box.PackStart  (row.ValueEntry, false, false, 0);
             button_box.PackStart (row.Buttons, false, false, 0);
 
+            if (terms.Count > 0) {
+                row.FieldChooser.Active = terms[terms.Count - 1].FieldChooser.Active;
+                row.OpChooser.Active = terms[terms.Count - 1].OpChooser.Active;
+            }
+
             row.Show ();
 
             row.CanDelete = canDelete;

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs	Tue Mar 25 03:57:07 2008
@@ -85,11 +85,19 @@
             types[entry_type] = query_value_type;
         }
 
+        public static Type GetValueType (QueryValueEntry entry)
+        {
+            return types [entry.GetType ()];
+        }
+
         static QueryValueEntry () {
             AddSubType (typeof(StringQueryValueEntry), typeof(StringQueryValue));
             AddSubType (typeof(IntegerQueryValueEntry), typeof(IntegerQueryValue));
             AddSubType (typeof(DateQueryValueEntry), typeof(DateQueryValue));
             AddSubType (typeof(FileSizeQueryValueEntry), typeof(FileSizeQueryValue));
+            AddSubType (typeof(TimeSpanQueryValueEntry), typeof(TimeSpanQueryValue));
+            AddSubType (typeof(RelativeTimeSpanQueryValueEntry), typeof(RelativeTimeSpanQueryValue));
+            AddSubType (typeof(NullQueryValueEntry), typeof(NullQueryValue));
         }
     }
 }

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/RelativeTimeSpanQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/RelativeTimeSpanQueryValueEntry.cs	Tue Mar 25 03:57:07 2008
@@ -0,0 +1,50 @@
+//
+// RelativeTimeSpanQueryValueEntry.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+using Mono.Unix;
+
+using Hyena.Query;
+using Gtk;
+
+namespace Hyena.Query.Gui
+{
+    public class RelativeTimeSpanQueryValueEntry : TimeSpanQueryValueEntry
+    {
+        public RelativeTimeSpanQueryValueEntry () : base ()
+        {
+            Add (new Label (Catalog.GetString ("ago")));
+        }
+
+        protected override void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetRelativeValue (-spin_button.ValueAsInt, factors [combo.Active]);
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/TimeSpanQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Query.Gui/TimeSpanQueryValueEntry.cs	Tue Mar 25 03:57:07 2008
@@ -0,0 +1,93 @@
+//
+// TimeSpanQueryValueEntry.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+using Mono.Unix;
+
+using Hyena.Query;
+using Gtk;
+
+namespace Hyena.Query.Gui
+{
+    public class TimeSpanQueryValueEntry : QueryValueEntry
+    {
+        protected SpinButton spin_button;
+        protected ComboBox combo;
+        protected TimeSpanQueryValue query_value;
+        private int set_combo = 1;
+
+        protected static readonly TimeFactor [] factors = new TimeFactor [] {
+            TimeFactor.Second, TimeFactor.Minute, TimeFactor.Hour, TimeFactor.Day,
+            TimeFactor.Week, TimeFactor.Month, TimeFactor.Year
+        };
+
+        public TimeSpanQueryValueEntry () : base ()
+        {
+            spin_button = new SpinButton (0.0, 1.0, 1.0);
+            spin_button.Digits = 0;
+            spin_button.WidthChars = 4;
+            spin_button.SetRange (0.0, Double.MaxValue);
+            Add (spin_button);
+
+            combo = ComboBox.NewText ();
+            combo.AppendText (Catalog.GetString ("seconds"));
+            combo.AppendText (Catalog.GetString ("minutes"));
+            combo.AppendText (Catalog.GetString ("hours"));
+            combo.AppendText (Catalog.GetString ("days"));
+            combo.AppendText (Catalog.GetString ("weeks"));
+            combo.AppendText (Catalog.GetString ("months"));
+            combo.AppendText (Catalog.GetString ("years"));
+            combo.Realized += delegate { combo.Active = set_combo; };
+            Add (combo);
+
+            spin_button.ValueChanged += HandleValueChanged;
+            combo.Changed += HandleValueChanged;
+        }
+
+        public override QueryValue QueryValue {
+            get { return query_value; }
+            set { 
+                spin_button.ValueChanged -= HandleValueChanged;
+                combo.Changed -= HandleValueChanged;
+
+                query_value = value as TimeSpanQueryValue;
+                spin_button.Value = query_value.FactoredValue;
+                combo.Active = set_combo = Array.IndexOf (factors, query_value.Factor);
+
+                spin_button.ValueChanged += HandleValueChanged;
+                combo.Changed += HandleValueChanged;
+            }
+        }
+
+        protected virtual void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetRelativeValue (spin_button.ValueAsInt, factors [combo.Active]);
+        }
+    }
+}

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am	Tue Mar 25 03:57:07 2008
@@ -40,12 +40,15 @@
 	Hyena.Query.Gui/DateQueryValueEntry.cs \
 	Hyena.Query.Gui/FileSizeQueryValueEntry.cs \
 	Hyena.Query.Gui/IntegerQueryValueEntry.cs \
+	Hyena.Query.Gui/NullQueryValueEntry.cs \
 	Hyena.Query.Gui/QueryBox.cs \
 	Hyena.Query.Gui/QueryLimitBox.cs \
 	Hyena.Query.Gui/QueryTermBox.cs \
 	Hyena.Query.Gui/QueryTermsBox.cs \
 	Hyena.Query.Gui/QueryValueEntry.cs \
+	Hyena.Query.Gui/RelativeTimeSpanQueryValueEntry.cs \
 	Hyena.Query.Gui/StringQueryValueEntry.cs \
+	Hyena.Query.Gui/TimeSpanQueryValueEntry.cs \
 	Hyena.Widgets/AnimatedBox.cs \
 	Hyena.Widgets/AnimatedHBox.cs \
 	Hyena.Widgets/AnimatedImage.cs \

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/DateQueryValue.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/DateQueryValue.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/DateQueryValue.cs	Tue Mar 25 03:57:07 2008
@@ -38,36 +38,23 @@
 
 namespace Hyena.Query
 {
-    public enum RelativeDateFactor {
-        Second = 1,
-        Minute = 60,
-        Hour   = 3600,
-        Day    = 3600*24,
-        Week   = 3600*24*7,
-        Month  = 3600*24*30,
-        Year   = 3600*24*365
-    }
-
     public class DateQueryValue : QueryValue
     {
-        public static readonly Operator Equal              = new Operator ("equals", "= {0}", "==", "=", ":");
-        public static readonly Operator NotEqual           = new Operator ("notEqual", "!= {0}", true, "!=", "!:");
-        public static readonly Operator LessThanEqual      = new Operator ("lessThanEquals", "<= {0}", "<=");
-        public static readonly Operator GreaterThanEqual   = new Operator ("greaterThanEquals", ">= {0}", ">=");
-        public static readonly Operator LessThan           = new Operator ("lessThan", "< {0}", "<");
-        public static readonly Operator GreaterThan        = new Operator ("greaterThan", "> {0}", ">");
-
-        protected DateTime value;
-        protected bool relative = false;
-        protected long offset = 0;
-        protected RelativeDateFactor factor;
+        //public static readonly Operator Equal              = new Operator ("equals", "= {0}", "==", "=", ":");
+        //public static readonly Operator NotEqual           = new Operator ("notEqual", "!= {0}", true, "!=", "!:");
+        //public static readonly Operator LessThanEqual      = new Operator ("lessThanEquals", "<= {0}", "<=");
+        //public static readonly Operator GreaterThanEqual   = new Operator ("greaterThanEquals", ">= {0}", ">=");
+        public static readonly Operator LessThan           = new Operator ("lessThan", "< {0}", "<"); // Before
+        public static readonly Operator GreaterThan        = new Operator ("greaterThan", "> {0}", ">"); // After
+
+        protected DateTime value = DateTime.Now;
 
         public override string XmlElementName {
             get { return "date"; }
         }
 
         //protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (Equal, NotEqual, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual);
-        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (LessThanEqual, GreaterThanEqual);
+        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (LessThan, GreaterThan);
         public override AliasedObjectSet<Operator> OperatorSet {
             get { return operators; }
         }
@@ -76,93 +63,40 @@
             get { return value; }
         }
 
-        public bool Relative {
-            get { return relative; }
-        }
-
-        public RelativeDateFactor Factor {
-            get { return factor; }
-        }
-
-        public long RelativeOffset {
-            get { return offset; }
-        }
-
-        private static Regex number_regex = new Regex ("\\d+", RegexOptions.Compiled);
         public override void ParseUserQuery (string input)
         {
             try {
                 value = DateTime.Parse (input);
                 IsEmpty = false;
             } catch {
-                Match match = number_regex.Match (input);
-                if (match != Match.Empty && match.Groups.Count > 0) {
-                    int val = Convert.ToInt32 (match.Groups[0].Captures[0].Value);
-                    foreach (RelativeDateFactor factor in Enum.GetValues (typeof(RelativeDateFactor))) {
-                        if (input == FactorString (factor, val)) {
-                            SetRelativeValue ((long) -val, factor);
-                            return;
-                        }
-                    }
-                }
                 IsEmpty = true;
             }
         }
 
         public override string ToUserQuery ()
         {
-            if (relative) {
-                return FactorString (factor, (int) (RelativeOffset == 0 ? 0 : (-RelativeOffset / (long) factor)));
+            if (value.Hour == 0 && value.Minute == 0 && value.Second == 0) {
+                return value.ToString ("yyyy-MM-dd");
             } else {
-                if (value.Hour == 0 && value.Minute == 0 && value.Second == 0) {
-                    return value.ToString ("yyyy-MM-dd");
-                } else {
-                    return value.ToString ();
-                }
+                return value.ToString ();
             }
         }
 
         public void SetValue (DateTime date)
         {
             value = date;
-            relative = false;
-            IsEmpty = false;
-        }
-
-        public void SetRelativeValue (long offset, RelativeDateFactor factor)
-        {
-            this.factor = factor;
-            this.offset = offset * (long) factor;
-            relative = true;
             IsEmpty = false;
         }
 
         public void LoadString (string val, bool isRelative)
         {
             try {
-                if (isRelative) {
-                    SetRelativeValue (Convert.ToInt64 (val), RelativeDateFactor.Second);
-                    DetermineFactor ();
-                } else {
-                    SetValue (DateTime.Parse (val));
-                }
+                SetValue (DateTime.Parse (val));
             } catch {
                 IsEmpty = true;
             }
         }
 
-        protected void DetermineFactor ()
-        {
-            if (relative) {
-                long val = Math.Abs (offset);
-                foreach (RelativeDateFactor factor in Enum.GetValues (typeof(RelativeDateFactor))) {
-                    if (val >= (long) factor) {
-                        this.factor = factor;
-                    }
-                }
-            }
-        }
-
         public override void ParseXml (XmlElement node)
         {
             try {
@@ -172,45 +106,13 @@
             }
         }
 
-        public override void AppendXml (XmlElement node)
-        {
-            if (relative) {
-                node.SetAttribute ("type", "rel");
-                node.InnerText = RelativeOffset.ToString ();
-            } else {
-                base.AppendXml (node);
-            }
-        }
-
         public override string ToSql ()
         {
-            return DateTimeUtil.FromDateTime (
-                (relative ? DateTime.Now + TimeSpan.FromSeconds ((double) offset) : value)
-            ).ToString ();
+            return DateTimeUtil.FromDateTime (value).ToString ();
         }
 
         public DateTime DateTime {
             get { return value; }
         }
-
-        protected static string FactorString (RelativeDateFactor factor, int count)
-        {
-            string translated = null;
-            switch (factor) {
-                case RelativeDateFactor.Second: translated = Catalog.GetPluralString ("{0} second", "{0} seconds", count); break;
-                case RelativeDateFactor.Minute: translated = Catalog.GetPluralString ("{0} minute", "{0} minutes", count); break;
-                case RelativeDateFactor.Hour:   translated = Catalog.GetPluralString ("{0} hour",   "{0} hours", count); break;
-                case RelativeDateFactor.Day:    translated = Catalog.GetPluralString ("{0} day",    "{0} days", count); break;
-                case RelativeDateFactor.Week:   translated = Catalog.GetPluralString ("{0} week",   "{0} weeks", count); break;
-                case RelativeDateFactor.Month:  translated = Catalog.GetPluralString ("{0} month",  "{0} months", count); break;
-                case RelativeDateFactor.Year:   translated = Catalog.GetPluralString ("{0} year",   "{0} years", count); break;
-                default: return null;
-            }
-
-            return String.Format (
-                Catalog.GetString ("{0} ago"),
-                String.Format (translated, count)
-            );
-        }
     }
 }

Added: trunk/banshee/src/Libraries/Hyena/Hyena.Query/NullQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/NullQueryValue.cs	Tue Mar 25 03:57:07 2008
@@ -0,0 +1,81 @@
+//
+// NullQueryValue.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Xml;
+using System.Text;
+
+using Hyena;
+
+namespace Hyena.Query
+{
+    public class NullQueryValue : QueryValue
+    {
+        public static readonly Operator IsNullOrEmpty  = new Operator ("empty", "IS NULL", "!");
+
+        public override string XmlElementName {
+            get { return "empty"; }
+        }
+
+        public override object Value {
+            get { return null; }
+        }
+
+        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (IsNullOrEmpty);
+        public override AliasedObjectSet<Operator> OperatorSet {
+            get { return operators; }
+        }
+
+        public NullQueryValue ()
+        {
+            IsEmpty = false;
+        }
+
+        public override void ParseUserQuery (string input)
+        {
+        }
+
+        public override void ParseXml (XmlElement node)
+        {
+        }
+
+        public override void AppendXml (XmlElement node)
+        {
+            node.InnerText = String.Empty;
+        }
+
+        public void SetValue (string str)
+        {
+        }
+
+        public override string ToSql ()
+        {
+            return null;
+        }
+    }
+}

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryField.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryField.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryField.cs	Tue Mar 25 03:57:07 2008
@@ -35,9 +35,9 @@
 {
     public class QueryField : IAliasedObject
     {
-        private Type value_type;
-        public Type ValueType {
-            get { return value_type; }
+        private Type [] value_types;
+        public Type [] ValueTypes {
+            get { return value_types; }
         }
 
         private string name;
@@ -77,30 +77,39 @@
         }
 
         public QueryField (string name, string label, string column, bool isDefault, params string [] aliases)
-            : this (name, label, column, typeof(StringQueryValue), isDefault, aliases)
+            : this (name, label, column, new Type [] {typeof(StringQueryValue)}, isDefault, aliases)
         {
         }
 
         public QueryField (string name, string label, string column, Type valueType, params string [] aliases)
-            : this (name, label, column, valueType, false, aliases)
+            : this (name, label, column, new Type [] {valueType}, false, aliases)
         {
         }
 
-        public QueryField (string name, string label, string column, Type valueType, bool isDefault, params string [] aliases)
+        public QueryField (string name, string label, string column, Type [] valueTypes, params string [] aliases)
+            : this (name, label, column, valueTypes, false, aliases)
+        {
+        }
+
+        public QueryField (string name, string label, string column, Type [] valueTypes, bool isDefault, params string [] aliases)
         {
             this.name = name;
             this.label = label;
             this.column = column;
-            this.value_type = valueType;
+            this.value_types = valueTypes;
             this.is_default = isDefault;
             this.aliases = aliases;
 
-            QueryValue.AddValueType (value_type);
+            foreach (Type value_type in valueTypes) {
+                QueryValue.AddValueType (value_type);
+            }
         }
 
-        public QueryValue CreateQueryValue ()
+        public IEnumerable<QueryValue> CreateQueryValues ()
         {
-            return Activator.CreateInstance (ValueType) as QueryValue;
+            foreach (Type type in ValueTypes) {
+                yield return Activator.CreateInstance (type) as QueryValue;
+            }
         }
 
         public string ToTermString (string op, string value)
@@ -113,7 +122,7 @@
             string value = qv.ToSql ();
             if (op == null) op = qv.OperatorSet.First;
             if (Column.IndexOf ("{0}") == -1 && Column.IndexOf ("{1}") == -1) {
-                if (ValueType == typeof(StringQueryValue)) {
+                if (qv is StringQueryValue) {
                     // Match string values literally and against a lower'd version 
                     return String.Format ("({0} {1} OR LOWER({0}) {2})", Column,
                         String.Format (op.SqlFormat, value),

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOperator.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOperator.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOperator.cs	Tue Mar 25 03:57:07 2008
@@ -43,7 +43,7 @@
 
         public string label;
         public string Label {
-            get { return label; }
+            get { return label ?? Name; }
             set { label = value; }
         }
 

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryTermNode.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryTermNode.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryTermNode.cs	Tue Mar 25 03:57:07 2008
@@ -48,20 +48,30 @@
             string field_alias = field_set.FindAlias (token);
             if (field_alias != null) {
                 term.Field = field_set [field_alias];
-                term.Value = term.Field.CreateQueryValue ();
-                string op_alias = term.Value.OperatorSet.FindAlias (token);
-                if (op_alias != null) {
-                    term.Operator = term.Value.OperatorSet [op_alias];
-                    int field_separator = token.IndexOf (op_alias);
-                    token = token.Substring (field_separator + op_alias.Length);
-                    term.Value.ParseUserQuery (token);
-                } else {
+
+                foreach (QueryValue val in term.Field.CreateQueryValues ()) {
+                    term.Value = val;
+
+                    string op_alias = term.Value.OperatorSet.FindAlias (token);
+                    if (op_alias != null) {
+                        term.Operator = term.Value.OperatorSet [op_alias];
+                        int field_separator = token.IndexOf (op_alias);
+                        string temp = token.Substring (field_separator + op_alias.Length);
+
+                        term.Value.ParseUserQuery (temp);
+
+                        if (!term.Value.IsEmpty) {
+                            break;
+                        }
+                    }
+
+                    term.Operator = null;
                     term.Value = null;
-                    term.Field = null;
                 }
             }
 
             if (term.Value == null) {
+                term.Field = null;
                 term.Value = QueryValue.CreateFromUserQuery (token, term.Field);
             }
 
@@ -74,8 +84,9 @@
 
         public override QueryNode Trim ()
         {
-            if (Parent != null && (qvalue == null || qvalue.IsEmpty || (field != null && op == null)))
+            if (Parent != null && (qvalue == null || qvalue.IsEmpty || (field != null && op == null))) {
                 Parent.RemoveChild (this);
+            }
             return this;
         }
         
@@ -121,8 +132,9 @@
 
         private bool EmitTermMatch (StringBuilder sb, QueryField field, bool emit_or)
         {
-            if (Value.IsEmpty)
+            if (Value.IsEmpty) {
                 return false;
+            }
 
             if (emit_or) {
                 sb.Append (" OR ");

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryValue.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryValue.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryValue.cs	Tue Mar 25 03:57:07 2008
@@ -47,16 +47,31 @@
 
         public static QueryValue CreateFromUserQuery (string input, QueryField field)
         {
-            QueryValue val = (field == null) ? new StringQueryValue () : field.CreateQueryValue ();
-            val.ParseUserQuery (input);
-            return val;
+            if (field == null) {
+                QueryValue val = new StringQueryValue ();
+                val.ParseUserQuery (input);
+                return val;
+            } else {
+                foreach (QueryValue val in field.CreateQueryValues ()) {
+                    val.ParseUserQuery (input);
+                    if (!val.IsEmpty) {
+                        return val;
+                    }
+                }
+            }
+
+            return null;
         }
 
         public static QueryValue CreateFromXml (XmlElement parent, QueryField field)
         {
             if (field != null) {
-                QueryValue val = field.CreateQueryValue ();
-                return CreateFromXml (val, parent) ? val : null;
+                foreach (QueryValue val in field.CreateQueryValues ()) {
+                    if (CreateFromXml (val, parent)) {
+                        return val;
+                    }
+                }
+                return null;
             } else {
                 foreach (Type subtype in subtypes) {
                     QueryValue val = Activator.CreateInstance (subtype) as QueryValue;

Added: trunk/banshee/src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs	Tue Mar 25 03:57:07 2008
@@ -0,0 +1,84 @@
+//
+// RelativeTimeSpanQueryValue.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2007-2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using System.Text;
+using System.Text.RegularExpressions;
+
+using Mono.Unix;
+
+using Hyena;
+
+namespace Hyena.Query
+{
+    public class RelativeTimeSpanQueryValue : TimeSpanQueryValue
+    {
+        // The SQL operators in these Operators are reversed from normal on purpose
+        public static new readonly Operator GreaterThan        = new Operator ("greaterThan", "< {0}", ">");
+        public static new readonly Operator LessThan           = new Operator ("lessThan", "> {0}", "<");
+        public static new readonly Operator GreaterThanEqual   = new Operator ("greaterThanEquals", "<= {0}", ">=");
+        public static new readonly Operator LessThanEqual      = new Operator ("lessThanEquals", ">= {0}", "<=");
+
+        protected static new AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (GreaterThan, LessThan, GreaterThanEqual, LessThanEqual);
+        public override AliasedObjectSet<Operator> OperatorSet {
+            get { return operators; }
+        }
+
+        public override string XmlElementName {
+            get { return "date"; }
+        }
+
+        public override long Offset {
+            get { return -offset; }
+        }
+
+        public override void SetUserRelativeValue (long offset, TimeFactor factor)
+        {
+            SetRelativeValue (-offset, factor);
+        }
+
+        public override void AppendXml (XmlElement node)
+        {
+            node.SetAttribute ("type", "rel");
+            node.InnerText = Value.ToString ();
+        }
+
+        public override string ToSql ()
+        {
+            return DateTimeUtil.FromDateTime (DateTime.Now + TimeSpan.FromSeconds ((double) offset)).ToString ();
+        }
+
+        protected override string FactorString (TimeFactor factor, int count)
+        {
+            string translated = base.FactorString (factor, count);
+            return (translated == null) ? null : String.Format (Catalog.GetString ("{0} ago"), translated);
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/Hyena/Hyena.Query/TimeSpanQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/TimeSpanQueryValue.cs	Tue Mar 25 03:57:07 2008
@@ -0,0 +1,173 @@
+//
+// TimeSpanQueryValue.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2007-2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using System.Text;
+using System.Text.RegularExpressions;
+
+using Mono.Unix;
+
+using Hyena;
+
+namespace Hyena.Query
+{
+    public enum TimeFactor {
+        Second = 1,
+        Minute = 60,
+        Hour   = 3600,
+        Day    = 3600*24,
+        Week   = 3600*24*7,
+        Month  = 3600*24*30,
+        Year   = 3600*24*365
+    }
+
+    public class TimeSpanQueryValue : IntegerQueryValue
+    {
+        /*public static readonly Operator Equal              = new Operator ("equals", "= {0}", "==", "=", ":");
+        public static readonly Operator NotEqual           = new Operator ("notEqual", "!= {0}", true, "!=", "!:");
+        public static readonly Operator LessThanEqual      = new Operator ("lessThanEquals", "<= {0}", "<=");
+        public static readonly Operator GreaterThanEqual   = new Operator ("greaterThanEquals", ">= {0}", ">=");
+        public static readonly Operator LessThan           = new Operator ("lessThan", "< {0}", "<");
+        public static readonly Operator GreaterThan        = new Operator ("greaterThan", "> {0}", ">");*/
+
+        protected long offset = 0;
+        protected TimeFactor factor = TimeFactor.Second;
+
+        public override string XmlElementName {
+            get { return "timespan"; }
+        }
+
+        //protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (Equal, NotEqual, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual);
+        protected static AliasedObjectSet<Operator> ops = new AliasedObjectSet<Operator> (LessThan, GreaterThan, LessThanEqual, GreaterThanEqual);
+        public override AliasedObjectSet<Operator> OperatorSet {
+            get { return operators; }
+        }
+
+        public override object Value {
+            get { return offset; }
+        }
+
+        public virtual long Offset {
+            get { return offset; }
+        }
+
+        public TimeFactor Factor {
+            get { return factor; }
+        }
+
+        public long FactoredValue {
+            get { return Offset / (long) factor; }
+        }
+
+        private static Regex number_regex = new Regex ("\\d+", RegexOptions.Compiled);
+        public override void ParseUserQuery (string input)
+        {
+            Match match = number_regex.Match (input);
+            if (match != Match.Empty && match.Groups.Count > 0) {
+                int val = Convert.ToInt32 (match.Groups[0].Captures[0].Value);
+                foreach (TimeFactor factor in Enum.GetValues (typeof(TimeFactor))) {
+                    if (input == FactorString (factor, val)) {
+                        SetUserRelativeValue ((long) val, factor);
+                        return;
+                    }
+                }
+            }
+            IsEmpty = true;
+        }
+
+        public override string ToUserQuery ()
+        {
+            return FactorString (factor, (int) FactoredValue);
+        }
+
+        public virtual void SetUserRelativeValue (long offset, TimeFactor factor)
+        {
+            SetRelativeValue (offset, factor);
+        }
+
+        public void SetRelativeValue (long offset, TimeFactor factor)
+        {
+            this.factor = factor;
+            this.offset = offset * (long) factor;
+            IsEmpty = false;
+        }
+
+        public void LoadString (string val)
+        {
+            try {
+                SetRelativeValue (Convert.ToInt64 (val), TimeFactor.Second);
+                DetermineFactor ();
+            } catch {
+                IsEmpty = true;
+            }
+        }
+
+        protected void DetermineFactor ()
+        {
+            long val = Math.Abs (offset);
+            foreach (TimeFactor factor in Enum.GetValues (typeof(TimeFactor))) {
+                if (val >= (long) factor) {
+                    this.factor = factor;
+                }
+            }
+        }
+
+        public override void ParseXml (XmlElement node)
+        {
+            try {
+                LoadString (node.InnerText);
+            } catch {
+                IsEmpty = true;
+            }
+        }
+
+        public override string ToSql ()
+        {
+            return Convert.ToString (offset * 1000);
+        }
+
+        protected virtual string FactorString (TimeFactor factor, int count)
+        {
+            string translated = null;
+            switch (factor) {
+                case TimeFactor.Second: translated = Catalog.GetPluralString ("{0} second", "{0} seconds", count); break;
+                case TimeFactor.Minute: translated = Catalog.GetPluralString ("{0} minute", "{0} minutes", count); break;
+                case TimeFactor.Hour:   translated = Catalog.GetPluralString ("{0} hour",   "{0} hours", count); break;
+                case TimeFactor.Day:    translated = Catalog.GetPluralString ("{0} day",    "{0} days", count); break;
+                case TimeFactor.Week:   translated = Catalog.GetPluralString ("{0} week",   "{0} weeks", count); break;
+                case TimeFactor.Month:  translated = Catalog.GetPluralString ("{0} month",  "{0} months", count); break;
+                case TimeFactor.Year:   translated = Catalog.GetPluralString ("{0} year",   "{0} years", count); break;
+                default: return null;
+            }
+
+            return String.Format (translated, count);
+        }
+    }
+}

Modified: trunk/banshee/src/Libraries/Hyena/Makefile.am
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Makefile.am	(original)
+++ trunk/banshee/src/Libraries/Hyena/Makefile.am	Tue Mar 25 03:57:07 2008
@@ -40,6 +40,7 @@
 	Hyena.Query/FileSizeQueryValue.cs \
 	Hyena.Query/IntegerKeyedObjectQueryValue.cs \
 	Hyena.Query/IntegerQueryValue.cs \
+	Hyena.Query/NullQueryValue.cs \
 	Hyena.Query/QueryField.cs \
 	Hyena.Query/QueryFieldSet.cs \
 	Hyena.Query/QueryLimit.cs \
@@ -51,7 +52,9 @@
 	Hyena.Query/QueryTermNode.cs \
 	Hyena.Query/QueryToken.cs \
 	Hyena.Query/QueryValue.cs \
+	Hyena.Query/RelativeTimeSpanQueryValue.cs \
 	Hyena.Query/StringQueryValue.cs \
+	Hyena.Query/TimeSpanQueryValue.cs \
 	Hyena.Query/UserQueryParser.cs \
 	Hyena.Query/XmlQueryParser.cs \
 	Hyena.SExpEngine/ArithmeticFunctionSet.cs \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]