banshee r3158 - in trunk/banshee: . src/Core/Banshee.Services src/Core/Banshee.Services/Banshee.Collection src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Query src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.ThickClient src/Core/Banshee.ThickClient/Banshee.Gui src/Core/Banshee.ThickClient/Banshee.Query.Gui src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui src/Core/Hyena src/Core/Hyena.Gui src/Core/Hyena.Gui/Hyena.Query.Gui src/Core/Hyena/Hyena.Query



Author: gburt
Date: Thu Feb  7 00:37:29 2008
New Revision: 3158
URL: http://svn.gnome.org/viewvc/banshee?rev=3158&view=rev

Log:
2008-02-06  Gabriel Burt  <gabriel burt gmail com>

	This commit fixes the last big hack in Hyena.Query - operators.
	Operaters are now defined per QueryValue subclass, so you you will never
	get a contains operator on an int field, for example.  A new Hyena.Query.Gui
	layer provides widegtry for editing queries while allowing custom types to
	be integrated seamlessly (see RatingQueryValueEntry and
	PlaylistQueryValueEntry).  You can see the progress of reimplementing
	smart playlist editing by trying to create a new one.  It is not by any
	means finished, so it doesn't actually save yet.

	* src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs:
	* src/Core/Banshee.Services/Banshee.Collection/TrackListModel.cs:
	* src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs:
	* src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs: 
	Move FieldSet, AlbumField, and ArtistField from TrackListDatabaseModel to
	BansheeQuery.

	* src/Core/Banshee.ThickClient/Banshee.Gui/SourceActions.cs: Show the
	smart playlist dialog on New Smart Playlist action.

	* src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs: Get
	ArtistField and AlbumField from BansheeQuery, not the
	TrackListDatabaseModel.

	* src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs: Get
	somewhat working with new Hyena.Query.Gui code.

	* src/Core/Banshee.Services/Makefile.am: New Banshee.Query classes.

	* src/Core/Banshee.Services/Banshee.Query/NaturalIntegerQueryValue.cs:
	* src/Core/Banshee.Services/Banshee.Query/YearQueryValue.cs:
	* src/Core/Banshee.Services/Banshee.Query/RatingQueryValue.cs:
	* src/Core/Banshee.Services/Banshee.Query/PlaylistQueryValue.cs:
	* src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs:
	Subclasses of existing Hyena.Query QueryValue types, providing limits
	and/or default values.

	* src/Core/Hyena.Gui/Makefile.am: New Hyena.Query.Gui classes.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs:
	* src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs:
	* src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs:
	* src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs: New gui
	classes that define the gui for editing a QueryValue type.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs: New VBox subclass that
	contains multiple QueryTermBoxes.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs: New class that
	represents one QueryTermNode, and one row/condition in the smart playlist
	editor.

	* src/Core/Banshee.ThickClient/Makefile.am:
	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs:
	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs:
	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs:
	New Banshee-specific query gui classes.

	* src/Core/Hyena/Makefile.am: 
	* src/Core/Hyena/Hyena.Query/AliasedObjectSet.cs: New generic class for
	containing a set of of aliased objects.  Used by QueryFieldSet and
	QueryValue's OperatorSet.

	* src/Core/Hyena/Hyena.Query/QueryValue.cs: Define an OperatorSet for each
	QueryValue type.

	* src/Core/Hyena/Hyena.Query/StringQueryValue.cs: Define OperatorSet.

	* src/Core/Hyena/Hyena.Query/IntegerQueryValue.cs: Add Min/Max/Default
	value properties, define OperatorSet.

	* src/Core/Hyena/Hyena.Query/DateQueryValue.cs: Actually use the
	RelativeDateFactor property.

	* src/Core/Hyena/Hyena.Query/QueryField.cs: Implement IAliasedObject.

	* src/Core/Hyena/Hyena.Query/QueryFieldSet.cs: Factor most of the set
	logic into AliasedObjectSet so it can be reused for Operators.

	* src/Core/Hyena/Hyena.Query/QueryOperator.cs: Add SqlFormat property
	instead of doing a switch in QueryTermNode to produce the right SQL for an
	operator.  Get rid of cruft.  Add ability to have multiple 'aliases'
	(user-entered query operators, eg :).

	* src/Core/Hyena/Hyena.Query/QueryTermNode.cs: Operators are scoped to
	value types, and have SqlFormat properties; use them.

	* src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs:  
	* src/Core/Hyena/Hyena.Query/XmlQueryParser.cs: Operators are scoped to
	value types now.


Added:
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/NaturalIntegerQueryValue.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/PlaylistQueryValue.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/RatingQueryValue.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/YearQueryValue.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/AliasedObjectSet.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/TrackListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs
   trunk/banshee/src/Core/Banshee.Services/Makefile.am
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/SourceActions.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am
   trunk/banshee/src/Core/Hyena.Gui/Makefile.am
   trunk/banshee/src/Core/Hyena/Hyena.Query/DateQueryValue.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/IntegerQueryValue.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryField.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryFieldSet.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryOperator.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryTermNode.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryValue.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/StringQueryValue.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/XmlQueryParser.cs
   trunk/banshee/src/Core/Hyena/Makefile.am

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	Thu Feb  7 00:37:29 2008
@@ -39,6 +39,7 @@
 using Hyena.Query;
 
 using Banshee.Base;
+using Banshee.Query;
 using Banshee.Database;
 
 namespace Banshee.Collection.Database
@@ -86,21 +87,21 @@
                 filter_query = null;
             } else {
                 Hyena.Query.UserQueryParser qp = new UserQueryParser (Filter);
-                QueryNode n = qp.BuildTree (field_set);
+                QueryNode n = qp.BuildTree (BansheeQuery.FieldSet);
 
-                filter_query = n.ToSql (field_set);
+                filter_query = n.ToSql (BansheeQuery.FieldSet);
 
                 /*
                 Console.WriteLine ("query: {0}", Filter);
-                Console.WriteLine ("Xml for Query: {0}", n.ToXml (field_set, true));
+                Console.WriteLine ("Xml for Query: {0}", n.ToXml (BansheeQuery.FieldSet, true));
                 Console.WriteLine ("Sql for Query: {0}", filter_query);
-                Hyena.Query.QueryParser qp2 = new XmlQueryParser (n.ToXml (field_set));
-                QueryNode n2 = qp2.BuildTree (field_set);
+                Hyena.Query.QueryParser qp2 = new XmlQueryParser (n.ToXml (BansheeQuery.FieldSet));
+                QueryNode n2 = qp2.BuildTree (BansheeQuery.FieldSet);
                 if (n2 != null) {
                     Console.WriteLine ("User query for Xml: {0}", n2.ToUserQuery ());
                 } else
                     Console.WriteLine ("n2 is null");
-                */
+                    */
 
                 if (filter_query.Length == 0)
                     filter_query = null;
@@ -435,114 +436,5 @@
         public string ReloadFragment {
             get { return reload_fragment; }
         }
-
-        public static QueryFieldSet FieldSet {
-            get { return field_set; }
-        }
-        
-        public override QueryField ArtistField {
-            get { return field_set["artist"]; }
-        }
-
-        public override QueryField AlbumField {
-            get { return field_set["album"]; }
-        }
-
-        protected static QueryFieldSet field_set = new QueryFieldSet (
-            new QueryField (
-                "artist", Catalog.GetString ("Artist"), "CoreArtists.Name", true,
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("by"), Catalog.GetString ("artist"), Catalog.GetString ("artists"),
-                "by", "artist", "artists"
-            ),
-            new QueryField (
-                "album", Catalog.GetString ("Album"), "CoreAlbums.Title", true,
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("on"), Catalog.GetString ("album"), Catalog.GetString ("from"),
-                "on", "album", "from", "albumtitle"
-            ),
-            new QueryField (
-                "disc", Catalog.GetString ("Disc"), "CoreTracks.Disc", typeof(IntegerQueryValue),
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("disc"), Catalog.GetString ("cd"), Catalog.GetString ("discnum"),
-                "disc", "cd", "discnum"
-            ),
-            new QueryField (
-                "title", Catalog.GetString ("Track Title"), "CoreTracks.Title", true,
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("title"), Catalog.GetString ("titled"), Catalog.GetString ("name"), Catalog.GetString ("named"),
-                "title", "titled", "name", "named"
-            ),
-            new QueryField (
-                "year", Catalog.GetString ("Year"), "CoreTracks.Year", typeof(IntegerQueryValue),
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("year"), Catalog.GetString ("released"), Catalog.GetString ("yr"),
-                "year", "released", "yr"
-            ),
-            new QueryField (
-                "rating", Catalog.GetString ("Rating"), "CoreTracks.Rating", typeof(IntegerQueryValue),
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("rating"), Catalog.GetString ("stars"),
-                "rating", "stars"
-            ),
-            new QueryField (
-                "playcount", Catalog.GetString ("Play Count"), "CoreTracks.PlayCount", typeof(IntegerQueryValue),
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("plays"), Catalog.GetString ("playcount"), Catalog.GetString ("listens"),
-                "plays", "playcount", "numberofplays", "listens"
-            ),
-            new QueryField (
-                "skipcount", Catalog.GetString ("Skip Count"), "CoreTracks.SkipCount", typeof(IntegerQueryValue),
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("skips"), Catalog.GetString ("skipcount"),
-                "skips", "skipcount"
-            ),
-            new QueryField (
-                "filesize", Catalog.GetString ("File Size"), "CoreTracks.FileSize", typeof(FileSizeQueryValue),
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("size"), Catalog.GetString ("filesize"),
-                "size", "filesize"
-            ),
-            new QueryField (
-                "uri", Catalog.GetString ("File Location"), "CoreTracks.Uri",
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("uri"), Catalog.GetString ("path"), Catalog.GetString ("file"), Catalog.GetString ("location"),
-                "uri", "path", "file", "location"
-            ),
-            new QueryField (
-                "duration", Catalog.GetString ("Duration"), "CoreTracks.Duration", typeof(IntegerQueryValue),
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("duration"), Catalog.GetString ("length"), Catalog.GetString ("time"),
-                "duration", "length", "time"
-            ),
-            new QueryField (
-                "mimetype", Catalog.GetString ("Mime Type"), "CoreTracks.MimeType {0} OR CoreTracks.Uri {0}",
-                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
-                Catalog.GetString ("type"), Catalog.GetString ("mimetype"), Catalog.GetString ("format"), Catalog.GetString ("ext"),
-                "type", "mimetype", "format", "ext", "mime"
-            ),
-            new QueryField (
-                "lastplayed", Catalog.GetString ("Last Played Date"), "CoreTracks.LastPlayedStamp", 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"
-            ),
-            new QueryField (
-                "added", Catalog.GetString ("Imported Date"), "CoreTracks.DateAddedStamp", 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"
-            ),
-            new QueryField (
-                "playlistid", Catalog.GetString ("Playlist"),
-                "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CorePlaylistEntries WHERE PlaylistID = {1})", typeof(IntegerQueryValue),
-                "playlistid", "playlist"
-            ),
-            new QueryField (
-                "smartplaylistid", Catalog.GetString ("Smart Playlist"),
-                "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CoreSmartPlaylistEntries WHERE SmartPlaylistID = {1})", typeof(IntegerQueryValue),
-                "smartplaylistid", "smartplaylist"
-            )
-        );
     }
 }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/TrackListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/TrackListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/TrackListModel.cs	Thu Feb  7 00:37:29 2008
@@ -58,8 +58,5 @@
         public virtual IEnumerable<AlbumInfo> AlbumInfoFilter {
             set { throw new NotImplementedException(); }
         }
-
-        public abstract QueryField ArtistField { get; }
-        public abstract QueryField AlbumField { get; }
     }
 }

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,152 @@
+//
+// BansheeQuery.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//   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.Data;
+using System.Text;
+using System.Collections.Generic;
+
+using Mono.Unix;
+
+using Hyena.Query;
+
+namespace Banshee.Query
+{
+    public static class BansheeQuery
+    {
+        public static QueryFieldSet FieldSet {
+            get { return field_set; }
+        }
+        
+        public static QueryField ArtistField {
+            get { return field_set["artist"]; }
+        }
+
+        public static QueryField AlbumField {
+            get { return field_set["album"]; }
+        }
+
+        private static QueryFieldSet field_set = new QueryFieldSet (
+            new QueryField (
+                "artist", Catalog.GetString ("Artist"), "CoreArtists.Name", true,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("artist"), Catalog.GetString ("by"), Catalog.GetString ("artists"),
+                "by", "artist", "artists"
+            ),
+            new QueryField (
+                "album", Catalog.GetString ("Album"), "CoreAlbums.Title", true,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("album"), Catalog.GetString ("on"), Catalog.GetString ("from"),
+                "on", "album", "from", "albumtitle"
+            ),
+            new QueryField (
+                "disc", Catalog.GetString ("Disc"), "CoreTracks.Disc", typeof(NaturalIntegerQueryValue),
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("disc"), Catalog.GetString ("cd"), Catalog.GetString ("discnum"),
+                "disc", "cd", "discnum"
+            ),
+            new QueryField (
+                "title", Catalog.GetString ("Track Title"), "CoreTracks.Title", true,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("title"), Catalog.GetString ("titled"), Catalog.GetString ("name"), Catalog.GetString ("named"),
+                "title", "titled", "name", "named"
+            ),
+            new QueryField (
+                "year", Catalog.GetString ("Year"), "CoreTracks.Year", typeof(YearQueryValue),
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("year"), Catalog.GetString ("released"), Catalog.GetString ("yr"),
+                "year", "released", "yr"
+            ),
+            new QueryField (
+                "rating", Catalog.GetString ("Rating"), "CoreTracks.Rating", typeof(RatingQueryValue),
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("rating"), Catalog.GetString ("stars"),
+                "rating", "stars"
+            ),
+            new QueryField (
+                "playcount", Catalog.GetString ("Play Count"), "CoreTracks.PlayCount", typeof(NaturalIntegerQueryValue),
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("plays"), Catalog.GetString ("playcount"), Catalog.GetString ("listens"),
+                "plays", "playcount", "numberofplays", "listens"
+            ),
+            new QueryField (
+                "skipcount", Catalog.GetString ("Skip Count"), "CoreTracks.SkipCount", typeof(NaturalIntegerQueryValue),
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("skips"), Catalog.GetString ("skipcount"),
+                "skips", "skipcount"
+            ),
+            new QueryField (
+                "filesize", Catalog.GetString ("File Size"), "CoreTracks.FileSize", typeof(FileSizeQueryValue),
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("size"), Catalog.GetString ("filesize"),
+                "size", "filesize"
+            ),
+            new QueryField (
+                "uri", Catalog.GetString ("File Location"), "CoreTracks.Uri",
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("uri"), Catalog.GetString ("path"), Catalog.GetString ("file"), Catalog.GetString ("location"),
+                "uri", "path", "file", "location"
+            ),
+            new QueryField (
+                "duration", Catalog.GetString ("Duration"), "CoreTracks.Duration", typeof(IntegerQueryValue),
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("duration"), Catalog.GetString ("length"), Catalog.GetString ("time"),
+                "duration", "length", "time"
+            ),
+            new QueryField (
+                "mimetype", Catalog.GetString ("Mime Type"), "CoreTracks.MimeType {0} OR CoreTracks.Uri {0}",
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("type"), Catalog.GetString ("mimetype"), Catalog.GetString ("format"), Catalog.GetString ("ext"),
+                "type", "mimetype", "format", "ext", "mime"
+            ),
+            new QueryField (
+                "lastplayed", Catalog.GetString ("Last Played Date"), "CoreTracks.LastPlayedStamp", 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"
+            ),
+            new QueryField (
+                "added", Catalog.GetString ("Imported Date"), "CoreTracks.DateAddedStamp", 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"
+            ),
+            new QueryField (
+                "playlistid", Catalog.GetString ("Playlist"),
+                "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CorePlaylistEntries WHERE PlaylistID = {1})", typeof(PlaylistQueryValue),
+                "playlistid", "playlist"
+            ),
+            new QueryField (
+                "smartplaylistid", Catalog.GetString ("Smart Playlist"),
+                "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CoreSmartPlaylistEntries WHERE SmartPlaylistID = {1})", typeof(SmartPlaylistQueryValue),
+                "smartplaylistid", "smartplaylist"
+            )
+        );
+    }
+}

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/NaturalIntegerQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/NaturalIntegerQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,43 @@
+//
+// NaturalIntegerQueryValue.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 Hyena.Query;
+
+namespace Banshee.Query
+{
+    public class NaturalIntegerQueryValue : IntegerQueryValue
+    {
+        public override long DefaultValue {
+            get { return 1; }
+        }
+
+        public override long MinValue {
+            get { return 0; }
+        }
+    }
+}

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/PlaylistQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/PlaylistQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,41 @@
+//
+// PlaylistQueryValue.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;
+
+namespace Banshee.Query
+{
+    public class PlaylistQueryValue : IntegerQueryValue
+    {
+        private static AliasedObjectSet<Operator> playlist_ops = new AliasedObjectSet<Operator> (Equal, NotEqual);
+        public override AliasedObjectSet<Operator> OperatorSet {
+            get { return playlist_ops; }
+        }
+    }
+}

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/RatingQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/RatingQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,47 @@
+//
+// RatingQueryValue.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 Hyena.Query;
+
+namespace Banshee.Query
+{
+    public class RatingQueryValue : IntegerQueryValue
+    {
+        public override long DefaultValue {
+            get { return 5; }
+        }
+
+        public override long MinValue {
+            get { return 0; }
+        }
+
+        public override long MaxValue {
+            get { return 5; }
+        }
+    }
+}

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,37 @@
+//
+// SmartPlaylistQueryValue.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;
+
+namespace Banshee.Query
+{
+    public class SmartPlaylistQueryValue : IntegerQueryValue
+    {
+    }
+}

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/YearQueryValue.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/YearQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,40 @@
+//
+// YearQueryValue.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;
+
+namespace Banshee.Query
+{
+    public class YearQueryValue : IntegerQueryValue
+    {
+        public override long DefaultValue {
+            get { return (long) DateTime.Now.Year; }
+        }
+    }
+}

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	Thu Feb  7 00:37:29 2008
@@ -39,6 +39,7 @@
 
 using Banshee.Collection.Database;
 using Banshee.ServiceStack;
+using Banshee.Query;
 
 namespace Banshee.SmartPlaylist
 {
@@ -143,20 +144,20 @@
                 foreach (QueryOperator op in QueryOperator.Operators) {
                     if (op.MatchesCondition (condition, out col, out v1, out v2)) {
                         QueryTermNode term = new QueryTermNode ();
-                        QueryField field = TrackListDatabaseModel.FieldSet [col];
+                        QueryField field = BansheeQuery.FieldSet [col];
                         bool is_relative_date = false;
                         if (field == null) {
                             if (col.IndexOf ("DateAddedStamp") != -1) {
-                                field = TrackListDatabaseModel.FieldSet.GetByAlias ("added");
+                                field = BansheeQuery.FieldSet ["added"];
                             } else if (col.IndexOf ("LastPlayedStamp") != -1) {
-                                field = TrackListDatabaseModel.FieldSet.GetByAlias ("lastplayed");
+                                field = BansheeQuery.FieldSet ["lastplayed"];
                             }
 
                             // Fix ugly implementation of playlist/smart playlist conditions
                             if (op == QueryOperator.InPlaylist || op == QueryOperator.NotInPlaylist) {
-                                field = TrackListDatabaseModel.FieldSet ["playlist"];
+                                field = BansheeQuery.FieldSet ["playlist"];
                             } else if (op == QueryOperator.InSmartPlaylist || op == QueryOperator.NotInSmartPlaylist) {
-                                field = TrackListDatabaseModel.FieldSet ["smartplaylist"];
+                                field = BansheeQuery.FieldSet ["smartplaylist"];
                             }
 
                             if (field == null) {
@@ -165,7 +166,6 @@
                             is_relative_date = true;
                         }
 
-                        term.Operator = Operator.GetByUserOperator (op.NewOp);
                         term.Field = field;
 
                         if (op == QueryOperator.Between) {
@@ -177,19 +177,21 @@
                                 ParseRelativeDateCondition (term, v1, field, ">=");
                                 ParseRelativeDateCondition (t2, v2, field, "<=");
                             } else {
-                                term.Operator = Operator.GetByUserOperator ("<=");
                                 term.Value = QueryValue.CreateFromUserQuery (v1, field);
-                                t2.Operator = Operator.GetByUserOperator (">=");
+                                term.Operator = term.Value.OperatorSet ["<="];
+
                                 t2.Value = QueryValue.CreateFromUserQuery (v2, field);
+                                t2.Operator = t2.Value.OperatorSet [">="];
                             }
                             and.AddChild (term);
                             and.AddChild (t2);
                             root.AddChild (and);
                         } else if (is_relative_date) {
-                            ParseRelativeDateCondition (term, v1, field, term.Operator.UserOperator);
+                            ParseRelativeDateCondition (term, v1, field, op.NewOp);
                             root.AddChild (term);
                         } else {
                             term.Value = QueryValue.CreateFromUserQuery (v1, field);
+                            term.Operator = term.Value.OperatorSet [op.NewOp];
                             root.AddChild (term);
                         }
 
@@ -203,21 +205,26 @@
             QueryNode node = root.Trim ();
 
             if (node != null) {
-                //Console.WriteLine ("After XML: {0}", node.ToXml (TrackListDatabaseModel.FieldSet, true));
-                //Console.WriteLine ("After SQL: {0}", node.ToSql (TrackListDatabaseModel.FieldSet));
+                //Console.WriteLine ("After XML: {0}", node.ToXml (BansheeQuery.FieldSet, true));
+                //Console.WriteLine ("After SQL: {0}", node.ToSql (BansheeQuery.FieldSet));
             }
 
-            return node == null ? String.Empty : node.ToXml (TrackListDatabaseModel.FieldSet);
+            return node == null ? String.Empty : node.ToXml (BansheeQuery.FieldSet);
         }
 
         private void ParseRelativeDateCondition (QueryTermNode term, string val, QueryField field, string op)
         {
-            // Have to flip the operator b/c of how we used to construct the SQL query
-            term.Operator = Operator.GetByUserOperator (op).Dual;
+            string new_op = op.Replace ('>', '^');
+            new_op = new_op.Replace ('<', '>');
+            new_op = new_op.Replace ('^', '<');
+
             DateQueryValue date_value = new DateQueryValue ();
+
+            // 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.RelativeOffset = -Convert.ToInt64 (val);
-            date_value.Relative = true;
+            date_value.SetRelativeValue (-Convert.ToInt64 (val), RelativeDateFactor.Second);
             term.Value = date_value;
         }
 

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs	Thu Feb  7 00:37:29 2008
@@ -38,6 +38,7 @@
 using Hyena.Data.Sqlite;
  
 using Banshee.Base;
+using Banshee.Query;
 using Banshee.Sources;
 using Banshee.Database;
 using Banshee.Playlist;
@@ -100,8 +101,8 @@
             set {
                 condition = value;
                 if (condition != null) {
-                    condition_sql = condition.ToSql (TrackListDatabaseModel.FieldSet);
-                    condition_xml = condition.ToXml (TrackListDatabaseModel.FieldSet);
+                    condition_sql = condition.ToSql (BansheeQuery.FieldSet);
+                    condition_xml = condition.ToXml (BansheeQuery.FieldSet);
                 }
             }
         }
@@ -116,7 +117,7 @@
             get { return condition_xml; }
             set {
                 condition_xml = value;
-                ConditionTree = XmlQueryParser.Parse (condition_xml, TrackListDatabaseModel.FieldSet);
+                ConditionTree = XmlQueryParser.Parse (condition_xml, BansheeQuery.FieldSet);
             }
         }
 

Modified: trunk/banshee/src/Core/Banshee.Services/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Makefile.am	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Makefile.am	Thu Feb  7 00:37:29 2008
@@ -85,6 +85,12 @@
 	Banshee.Playlists.Formats/PlaylistFormatDescription.cs \
 	Banshee.Playlists.Formats/PlaylistParser.cs \
 	Banshee.Playlists.Formats/PlsPlaylistFormat.cs \
+	Banshee.Query/BansheeQuery.cs \
+	Banshee.Query/NaturalIntegerQueryValue.cs \
+	Banshee.Query/PlaylistQueryValue.cs \
+	Banshee.Query/RatingQueryValue.cs \
+	Banshee.Query/SmartPlaylistQueryValue.cs \
+	Banshee.Query/YearQueryValue.cs \
 	Banshee.ServiceStack/Application.cs \
 	Banshee.ServiceStack/DBusServiceManager.cs \
 	Banshee.ServiceStack/IDBusExportable.cs \

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/SourceActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/SourceActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/SourceActions.cs	Thu Feb  7 00:37:29 2008
@@ -123,8 +123,8 @@
 
         private void OnNewSmartPlaylist (object o, EventArgs args)
         {
-            //Editor ed = new Editor ();
-            //ed.RunDialog ();
+            Editor ed = new Editor ();
+            ed.RunDialog ();
 
             /*Editor ed = new Editor ();
             ed.SetQueryFromSearch ();

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs	Thu Feb  7 00:37:29 2008
@@ -32,6 +32,7 @@
 using Gtk;
 
 using Banshee.Base;
+using Banshee.Query;
 using Banshee.Sources;
 using Banshee.Library;
 using Banshee.Playlist;
@@ -355,7 +356,7 @@
             Source source = ServiceManager.SourceManager.ActiveSource;
             ITrackModelSource track_source = source as ITrackModelSource;
             foreach (TrackInfo track in TrackSelector.GetSelectedTracks ()) {
-                source.FilterQuery = track_source.TrackModel.ArtistField.ToTermString (":", track.ArtistName);
+                source.FilterQuery = BansheeQuery.ArtistField.ToTermString (":", track.ArtistName);
                 break;
             }
         }
@@ -365,7 +366,7 @@
             Source source = ServiceManager.SourceManager.ActiveSource;
             ITrackModelSource track_source = source as ITrackModelSource;
             foreach (TrackInfo track in TrackSelector.GetSelectedTracks ()) {
-                source.FilterQuery = track_source.TrackModel.AlbumField.ToTermString (":", track.AlbumTitle);
+                source.FilterQuery = BansheeQuery.AlbumField.ToTermString (":", track.AlbumTitle);
                 break;
             }
         }

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,72 @@
+//
+// BansheeQueryBox.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 Mono.Unix;
+
+using Hyena.Query;
+using Hyena.Query.Gui;
+
+using Banshee.Query;
+
+namespace Banshee.Query.Gui
+{
+    public class BansheeQueryBox : QueryBox
+    {
+        public BansheeQueryBox () : base (BansheeQuery.FieldSet)
+        {
+        }
+
+        static BansheeQueryBox () {
+            // Register our custom query value entries
+            QueryValueEntry.AddSubType (typeof(RatingQueryValueEntry), typeof(RatingQueryValue));
+            QueryValueEntry.AddSubType (typeof(PlaylistQueryValueEntry), typeof(PlaylistQueryValue));
+
+            // Set translated names for operators
+            IntegerQueryValue.Equal.Label            = Catalog.GetString ("is");
+            IntegerQueryValue.NotEqual.Label         = Catalog.GetString ("is not");
+            IntegerQueryValue.LessThanEqual.Label    = Catalog.GetString ("less than");
+            IntegerQueryValue.GreaterThanEqual.Label = Catalog.GetString ("more than");
+            IntegerQueryValue.LessThan.Label         = Catalog.GetString ("at most");
+            IntegerQueryValue.GreaterThan.Label      = Catalog.GetString ("at least");
+
+            DateQueryValue.Equal.Label               = Catalog.GetString ("is");
+            DateQueryValue.NotEqual.Label            = Catalog.GetString ("is not");
+            DateQueryValue.LessThanEqual.Label       = Catalog.GetString ("less than");
+            DateQueryValue.GreaterThanEqual.Label    = Catalog.GetString ("more than");
+            DateQueryValue.LessThan.Label            = Catalog.GetString ("at most");
+            DateQueryValue.GreaterThan.Label         = Catalog.GetString ("at least");
+
+            StringQueryValue.Equal.Label             = Catalog.GetString ("is");
+            StringQueryValue.NotEqual.Label          = Catalog.GetString ("is not");
+            StringQueryValue.Contains.Label          = Catalog.GetString ("contains");
+            StringQueryValue.DoesNotContain.Label    = Catalog.GetString ("doesn't contain");
+            StringQueryValue.StartsWith.Label        = Catalog.GetString ("starts with");
+            StringQueryValue.EndsWith.Label          = Catalog.GetString ("ends with");
+        }
+    }
+}

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,92 @@
+//
+// PlaylistQueryValueEntry.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.Collections.Generic;
+using Gtk;
+
+using Hyena.Query;
+using Hyena.Query.Gui;
+
+using Banshee.ServiceStack;
+using Banshee.Sources;
+using Banshee.Playlist;
+using Banshee.Widgets;
+using Banshee.Query;
+
+namespace Banshee.Query.Gui
+{
+    public class PlaylistQueryValueEntry : QueryValueEntry
+    {
+        protected ComboBox combo;
+        protected PlaylistQueryValue query_value;
+        protected Dictionary<int, int> playlist_id_combo_map = new Dictionary<int, int> ();
+        protected Dictionary<int, int> combo_playlist_id_map = new Dictionary<int, int> ();
+
+        public PlaylistQueryValueEntry () : base ()
+        {
+            combo = ComboBox.NewText();
+
+            int count = 0;
+            PlaylistSource playlist;
+            foreach (Source child in ServiceManager.SourceManager.DefaultSource.Children) {
+                playlist = child as PlaylistSource;
+                if (playlist != null && playlist.DbId != null) {
+                    combo.AppendText (playlist.Name);
+                    playlist_id_combo_map [(int)playlist.DbId] = count;
+                    combo_playlist_id_map [count++] = (int) playlist.DbId;
+                }
+            }
+
+            Add (combo);
+            combo.Active = 0;
+
+            combo.Changed += HandleValueChanged;
+
+        }
+
+        public override QueryValue QueryValue {
+            get { return query_value; }
+            set { 
+                combo.Changed -= HandleValueChanged;
+                query_value = value as PlaylistQueryValue;
+                if (!query_value.IsEmpty) {
+                    try {
+                        combo.Active = playlist_id_combo_map [(int)query_value.IntValue];
+                    } catch {}
+                }
+                combo.Changed += HandleValueChanged;
+            }
+        }
+
+        protected void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetValue (combo_playlist_id_map [combo.Active]);
+        }
+    }
+}

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,68 @@
+//
+// RatingQueryValueEntry.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 Gtk;
+
+using Hyena.Query;
+using Hyena.Query.Gui;
+
+using Banshee.Widgets;
+using Banshee.Query;
+
+namespace Banshee.Query.Gui
+{
+    public class RatingQueryValueEntry : QueryValueEntry
+    {
+        protected RatingEntry entry;
+        protected RatingQueryValue query_value;
+
+        public RatingQueryValueEntry () : base ()
+        {
+            entry = new RatingEntry ();
+            entry.Changed += HandleValueChanged;
+
+            Add (entry);
+        }
+
+        public override QueryValue QueryValue {
+            get { return query_value; }
+            set { 
+                entry.Changed -= HandleValueChanged;
+                query_value = value as RatingQueryValue;
+                entry.Value = (int) (query_value.IsEmpty ? query_value.DefaultValue : query_value.IntValue);
+                entry.Changed += HandleValueChanged;
+            }
+        }
+
+        protected void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetValue (entry.Value);
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs	Thu Feb  7 00:37:29 2008
@@ -4,19 +4,24 @@
 using Glade;
 
 using Mono.Unix;
+
+using Hyena.Query;
+using Hyena.Query.Gui;
  
 using Banshee.Base;
+using Banshee.Query;
 using Banshee.Widgets;
 using Banshee.Sources;
 using Banshee.Database;
 using Banshee.Gui.Dialogs;
+using Banshee.Query.Gui;
 
 namespace Banshee.SmartPlaylist
 {
     public class Editor : GladeDialog
     {
-        private QueryBuilder builder;
-        private TracksQueryModel model;
+        private BansheeQueryBox builder;
+        //private TracksQueryModel model;
 
         private SmartPlaylistSource playlist = null;
 
@@ -36,10 +41,10 @@
             Dialog.Title = Catalog.GetString ("Edit Smart Playlist");
 
             name_entry.Text = playlist.Name;
-            Condition = playlist.Condition;
-            OrderBy = playlist.OrderBy;
-            LimitNumber = playlist.LimitNumber;
-            LimitCriterion = playlist.LimitCriterion;
+            //Condition = playlist.Condition;
+            //OrderBy = playlist.OrderBy;
+            //LimitNumber = playlist.LimitNumber;
+            //LimitCriterion = playlist.LimitCriterion;
         }
     
         public Editor () : base("SmartPlaylistEditorDialog")
@@ -52,8 +57,8 @@
             Dialog.Title = Catalog.GetString ("New Smart Playlist");
 
             // Add the QueryBuilder widget
-            model = new TracksQueryModel(this.playlist);
-            builder = new QueryBuilder(model);
+            //model = new TracksQueryModel(this.playlist);
+            builder = new BansheeQueryBox ();
             builder.Show();
             builder.Spacing = 4;
 
@@ -126,7 +131,7 @@
             name_entry.GrabFocus();
         }
 
-        public void SetQueryFromSearch()
+        /*public void SetQueryFromSearch()
         {
             Banshee.Widgets.SearchEntry search_entry = InterfaceElements.SearchEntry;
 
@@ -177,11 +182,11 @@
                 }
             }
 
-            Condition = condition;
+            //Condition = condition;
 
             Dialog.Title = Catalog.GetString ("Create Smart Playlist from Search");
             name_entry.Text = search_entry.GetLabelForFilterID(search_entry.ActiveFilterID) + ": " + query;
-        }
+        }*/
 
         public void RunDialog()
         {
@@ -199,26 +204,35 @@
             //dialog.GetSize (out w, out h);
             //Console.WriteLine ("w = {0}, h = {1}", w, h);
 
+            QueryNode node = builder.BuildQuery ();
+            if (node == null) {
+                Console.WriteLine ("Editor query is null");
+            } else {
+                Console.WriteLine ("Editor query is: {0}", node.ToXml (BansheeQuery.FieldSet, true));
+            }
+
             if (response == ResponseType.Ok) {
                 string name = PlaylistName;
-                string condition = Condition;
-                string order_by = OrderBy;
-                string limit_number = LimitNumber;
-                int limit_criterion = LimitCriterion;
+                //string condition = Condition;
+                //string order_by = OrderBy;
+                //string limit_number = LimitNumber;
+                //int limit_criterion = LimitCriterion;
 
                 ThreadAssist.Spawn (delegate {
                     //Console.WriteLine ("Name = {0}, Cond = {1}, OrderAndLimit = {2}", name, condition, order_by, limit_number);
                     if (playlist == null) {
+                    /*
                         Timer t = new Timer ("Create/Add new Playlist");
-                        playlist = new SmartPlaylistSource(name, condition, order_by, limit_number, limit_criterion);
-                        Banshee.Sources.LibrarySource.Instance.AddChildSource(playlist);
+                        //playlist = new SmartPlaylistSource(name, condition, order_by, limit_number, limit_criterion);
+                        //Banshee.Sources.LibrarySource.Instance.AddChildSource(playlist);
                         SmartPlaylistCore.Instance.StartTimer(playlist);
 
                         // Add this source to the source manager, otherwise it will be ignored until we restart
                         SourceManager.AddSource (playlist, false);
                         t.Stop();
+                        */
                     } else {
-                        playlist.Rename(name);
+                        /*playlist.Rename(name);
                         playlist.Condition = condition;
                         playlist.OrderBy = order_by;
                         playlist.LimitNumber = limit_number;
@@ -233,7 +247,7 @@
                             SmartPlaylistCore.Instance.StopTimer();
 
                         playlist.ListenToPlaylists();
-                        SmartPlaylistCore.Instance.SortPlaylists();
+                        SmartPlaylistCore.Instance.SortPlaylists();*/
                     }
                 });
             }
@@ -257,7 +271,7 @@
         {
             TreePath [] paths = adv_tree_view.Selection.GetSelectedRows ();
 
-            foreach (TreePath path in paths) {
+            /*foreach (TreePath path in paths) {
                 TreeIter iter;
                 if (adv_tree_view.Model.GetIter (out iter, path)) {
                     string name            = adv_tree_view.Model.GetValue (iter, 0) as string;
@@ -270,7 +284,7 @@
                     Banshee.Sources.LibrarySource.Instance.AddChildSource (pl);
                     SmartPlaylistCore.Instance.StartTimer (pl);
                 }
-            }
+            }*/
 
             Dialog.Destroy();
         }
@@ -283,13 +297,14 @@
                 return;
 
             TreeIter iter;
+            /*
             if (adv_tree_view.Model.GetIter (out iter, paths[0])) {
                 PlaylistName    = adv_tree_view.Model.GetValue (iter, 0) as string;
                 Condition       = adv_tree_view.Model.GetValue (iter, 1) as string;
                 OrderBy         = adv_tree_view.Model.GetValue (iter, 2) as string;
                 LimitNumber     = adv_tree_view.Model.GetValue (iter, 3) as string;
                 LimitCriterion  = (int) adv_tree_view.Model.GetValue (iter, 4);
-            }
+            }*/
         }
 
         private void HandleNameChanged(object sender, EventArgs args)
@@ -303,7 +318,7 @@
                 ok_button.Sensitive = false;
                 //already_in_use_label.Markup = "";
             } else {
-                object res = Globals.Library.Db.QuerySingle(new DbCommand(
+                /*object res = Globals.Library.Db.QuerySingle(new DbCommand(
                     "SELECT Name FROM SmartPlaylists WHERE lower(Name) = lower(:name)",
                     "name", name_entry.Text
                 ));
@@ -315,6 +330,7 @@
                     ok_button.Sensitive = true;
                     //already_in_use_label.Markup = "";
                 }
+                */
             }
         }
 
@@ -328,7 +344,7 @@
             }
         }
 
-        private string Condition {
+        /*private string Condition {
             get {
                 return builder.MatchesEnabled
                     ? builder.MatchQuery
@@ -377,6 +393,6 @@
             set {
                 builder.LimitCriterion = Convert.ToInt32 (value);
             }
-        }
+        }*/
     }
 }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am	Thu Feb  7 00:37:29 2008
@@ -61,6 +61,10 @@
 	Banshee.Library.Gui/FileImportSource.cs \
 	Banshee.Library.Gui/FolderImportSource.cs \
 	Banshee.Library.Gui/ImportDialog.cs \
+	Banshee.Query.Gui/BansheeQueryBox.cs \
+	Banshee.Query.Gui/RatingQueryValueEntry.cs \
+	Banshee.Query.Gui/PlaylistQueryValueEntry.cs \
+	Banshee.SmartPlaylist.Gui/Editor.cs \
 	Banshee.Sources.Gui/CellEditEntry.cs \
 	Banshee.Sources.Gui/SourceRowRenderer.cs \
 	Banshee.Sources.Gui/SourceView.cs \

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,95 @@
+//
+// DateQueryValueEntry.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 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
+        };
+
+        // 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"));
+            Add (combo);
+
+            Add (new Label ("ago"));
+
+            combo.Active = 1;
+
+            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 DateQueryValue;
+                //spin_button.Value = (double) (query_value.IsEmpty ? query_value.DefaultValue : query_value.IntValue);
+                combo.Active = Array.IndexOf (factors, query_value.Factor);
+                spin_button.ValueChanged += HandleValueChanged;
+                combo.Changed += HandleValueChanged;
+            }
+        }
+
+        protected void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetRelativeValue (spin_button.ValueAsInt, factors [combo.Active]);
+        }
+    }
+}

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,67 @@
+//
+// IntegerQueryValueEntry.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 IntegerQueryValueEntry : QueryValueEntry
+    {
+        protected SpinButton spin_button;
+        protected IntegerQueryValue query_value;
+
+        public IntegerQueryValueEntry () : base ()
+        {
+            spin_button = new SpinButton (0.0, 1.0, 1.0);
+            spin_button.Digits = 0;
+            spin_button.WidthChars = 4;
+            spin_button.ValueChanged += HandleValueChanged;
+
+            Add (spin_button);
+        }
+
+        public override QueryValue QueryValue {
+            get { return query_value; }
+            set { 
+                spin_button.ValueChanged -= HandleValueChanged;
+                query_value = value as IntegerQueryValue;
+                spin_button.SetRange (query_value.MinValue, query_value.MaxValue);
+                spin_button.Value = (double) (query_value.IsEmpty ? query_value.DefaultValue : query_value.IntValue);
+                spin_button.ValueChanged += HandleValueChanged;
+            }
+        }
+
+        protected void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetValue (spin_button.ValueAsInt);
+        }
+    }
+}

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,99 @@
+//
+// QueryBox.cs
+//
+// Authors:
+//   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2005-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.Text;
+
+using Gtk;
+using Hyena;
+using Hyena.Query;
+
+namespace Hyena.Query.Gui
+{
+    public class QueryBox : VBox
+    {
+        private QueryFieldSet field_set;
+
+        private QueryTermBox first_row = null;
+        public QueryTermBox FirstRow {
+            get { return first_row; }
+        }
+        
+        public QueryBox (QueryFieldSet fieldSet) : base()
+        {
+            this.field_set = fieldSet;
+            CreateRow (false);
+        }
+        
+        public void CreateRow (bool canDelete)
+        {
+            QueryTermBox row = new QueryTermBox (field_set);
+            row.Show();
+            PackStart (row, false, false, 0);
+            row.CanDelete = canDelete;
+            row.AddRequest += OnRowAddRequest;
+            row.RemoveRequest += OnRowRemoveRequest;
+
+            if (first_row == null) {
+                first_row = row;
+                //row.FieldBox.GrabFocus();
+            }
+        }
+        
+        public void OnRowAddRequest(object o, EventArgs args)
+        {
+            CreateRow (true);
+            UpdateCanDelete ();
+        }
+        
+        public void OnRowRemoveRequest(object o, EventArgs args)
+        {
+            Remove (o as Widget);
+            UpdateCanDelete ();
+        }
+        
+        public void UpdateCanDelete()
+        {
+            ((QueryTermBox) Children[0]).CanDelete = Children.Length > 1;
+        }
+        
+        public QueryNode BuildQuery ()
+        {
+            QueryListNode and = new QueryListNode (Keyword.And);
+
+            for (int i = 0, n = Children.Length; i < n; i++) {
+                QueryTermBox term_box = Children [i] as QueryTermBox;
+
+                and.AddChild (term_box.GetTermNode ());
+            }
+
+            return and.Trim ();
+        }
+    }
+}

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,177 @@
+//
+// QueryTermBox.cs
+//
+// Authors:
+//   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2005-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.Text;
+
+using Gtk;
+using Hyena;
+using Hyena.Query;
+
+namespace Hyena.Query.Gui
+{
+    public class QueryTermBox : HBox
+    {
+        private ComboBox field_chooser;
+        private ComboBox op_chooser;
+        private HBox value_box;
+
+        private Button add_button;
+        private Button remove_button;
+        
+        public event EventHandler AddRequest;
+        public event EventHandler RemoveRequest;
+
+        private QueryField field;
+        private QueryValueEntry value_entry;
+        private Operator op;
+
+        private QueryField [] sorted_fields;
+        private Operator [] operators;
+
+        public QueryTermBox (QueryFieldSet fieldSet) : base ()
+        {
+            this.sorted_fields = fieldSet.Fields;
+            //this.field_set = fieldSet;
+            BuildInterface ();
+        }
+
+        private void BuildInterface ()
+        {
+            Spacing = 5;
+
+            field_chooser = ComboBox.NewText ();
+            field_chooser.Changed += HandleFieldChanged;
+            PackStart (field_chooser, false, false, 0);
+            
+            op_chooser = ComboBox.NewText ();
+            op_chooser.Changed += HandleOperatorChanged;
+            PackStart (op_chooser, false, false, 0);
+            
+            value_box = new HBox ();
+            PackStart (value_box, false, false, 0);
+
+            remove_button = new Button (new Image ("gtk-remove", IconSize.Button));
+            remove_button.Relief = ReliefStyle.None;
+            remove_button.Clicked += OnButtonRemoveClicked;
+            PackEnd (remove_button, false, false, 0);
+            
+            add_button = new Button (new Image ("gtk-add", IconSize.Button));
+            add_button.Relief = ReliefStyle.None;
+            add_button.Clicked += OnButtonAddClicked;
+            PackEnd (add_button, false, false, 0);
+
+            foreach (QueryField field in sorted_fields) {
+                field_chooser.AppendText (field.Label);
+            }
+
+            field_chooser.Active = 0;
+
+            ShowAll ();
+        }
+        
+        private bool first = true;
+        private void HandleFieldChanged (object o, EventArgs args)
+        {
+            QueryField field = sorted_fields [field_chooser.Active];
+            Console.WriteLine ("Changing to field {0}", field.Name);
+            // Leave everything as is unless the new field is a different type
+            if (this.field != null && (field == this.field || field.ValueType == this.field.ValueType))
+                return;
+
+            op_chooser.Changed -= HandleOperatorChanged;
+
+            this.field = field;
+
+            if (first) {
+                first = false;
+            } else {
+                value_box.Remove (value_box.Children [0]);
+            }
+
+            value_entry = QueryValueEntry.Create (this.field.CreateQueryValue ());
+
+            if (value_entry == null) {
+                Console.WriteLine ("Value entry is NULL!, field.ValueType == {0}", this.field.ValueType);
+            }
+            value_box.Add (value_entry);
+            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);
+            }
+            
+            // TODO: If we have the same operator that was previously selected, select it
+            op_chooser.Changed += HandleOperatorChanged;
+            op_chooser.Active = 0;
+        }
+        
+        private void HandleOperatorChanged (object o, EventArgs args)
+        {
+            this.op = value_entry.QueryValue.OperatorSet.Objects [op_chooser.Active];
+            Console.WriteLine ("Operator {0} selected", op.Name);
+
+            //value_entry = new QueryValueEntry <field.ValueType> ();
+        }
+        
+        private void OnButtonAddClicked (object o, EventArgs args)
+        {
+            EventHandler handler = AddRequest;
+            if (handler != null)
+                handler (this, new EventArgs ());
+        }
+        
+        private void OnButtonRemoveClicked (object o, EventArgs args)
+        {
+            EventHandler handler = RemoveRequest;
+            if (handler != null)
+                handler (this, new EventArgs ());
+        }
+        
+        public bool CanDelete {
+            get { return remove_button.Sensitive; }
+            set { remove_button.Sensitive = value; }
+        }
+        
+        public QueryNode GetTermNode ()
+        {
+            QueryTermNode node = new QueryTermNode ();
+            node.Field = field;
+            node.Operator = op;
+            node.Value = value_entry.QueryValue;
+            return node;
+        }
+    }
+}

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,89 @@
+//
+// QueryValueEntry.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.Collections.Generic;
+
+using Hyena.Query;
+using Gtk;
+
+namespace Hyena.Query.Gui
+{
+    public abstract class QueryValueEntry : HBox
+    {
+        private static Dictionary<Type, Type> types = new Dictionary<Type, Type> ();
+
+        public QueryValueEntry () : base ()
+        {
+        }
+
+        public abstract QueryValue QueryValue { get; set; }
+
+        public static QueryValueEntry Create (QueryValue qv)
+        {
+            Type qv_type = qv.GetType ();
+            Type entry_type = null;
+
+            foreach (KeyValuePair<Type, Type> pair in types) {
+                if (pair.Value == qv_type) {
+                    entry_type = pair.Key;
+                    break;
+                }
+            }
+
+            // If we don't have an entry type that's exactly for our type, take a more generic one
+            if (entry_type == null) {
+                foreach (KeyValuePair<Type, Type> pair in types) {
+                    if (qv_type.IsSubclassOf (pair.Value)) {
+                        entry_type = pair.Key;
+                        break;
+                    }
+                }
+            }
+
+            if (entry_type != null) {
+                QueryValueEntry entry = Activator.CreateInstance (entry_type) as QueryValueEntry;
+                entry.QueryValue = qv;
+                return entry;
+            }
+
+            return null;
+        }
+
+        public static void AddSubType (Type entry_type, Type query_value_type)
+        {
+            types[entry_type] = query_value_type;
+        }
+
+        static QueryValueEntry () {
+            AddSubType (typeof(StringQueryValueEntry), typeof(StringQueryValue));
+            AddSubType (typeof(IntegerQueryValueEntry), typeof(IntegerQueryValue));
+            AddSubType (typeof(DateQueryValueEntry), typeof(DateQueryValue));
+        }
+    }
+}

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,63 @@
+//
+// StringQueryValueEntry.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 StringQueryValueEntry : QueryValueEntry
+    {
+        protected Gtk.Entry entry;
+        protected StringQueryValue query_value;
+
+        public StringQueryValueEntry () : base ()
+        {
+            entry = new Entry ();
+            entry.Changed += HandleChanged;
+            Add (entry);
+        }
+
+        public override QueryValue QueryValue {
+            get { return query_value; }
+            set { 
+                entry.Changed -= HandleChanged;
+                query_value = value as StringQueryValue;
+                entry.Text = (query_value.Value as string) ?? String.Empty;
+                entry.Changed += HandleChanged;
+            }
+        }
+
+        protected void HandleChanged (object o, EventArgs args)
+        {
+            query_value.SetValue (entry.Text);
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Hyena.Gui/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Hyena.Gui/Makefile.am	(original)
+++ trunk/banshee/src/Core/Hyena.Gui/Makefile.am	Thu Feb  7 00:37:29 2008
@@ -25,6 +25,12 @@
 	Hyena.Gui/EntryInsertAction.cs \
 	Hyena.Gui/EntryUndoAdapter.cs \
 	Hyena.Gui/GtkUtilities.cs \
+	Hyena.Query.Gui/DateQueryValueEntry.cs \
+	Hyena.Query.Gui/IntegerQueryValueEntry.cs \
+	Hyena.Query.Gui/QueryBox.cs \
+	Hyena.Query.Gui/QueryTermBox.cs \
+	Hyena.Query.Gui/QueryValueEntry.cs \
+	Hyena.Query.Gui/StringQueryValueEntry.cs \
 	Hyena.Widgets/RoundedFrame.cs \
 	Hyena.Widgets/ScrolledWindow.cs
 

Added: trunk/banshee/src/Core/Hyena/Hyena.Query/AliasedObjectSet.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/AliasedObjectSet.cs	Thu Feb  7 00:37:29 2008
@@ -0,0 +1,110 @@
+//
+// AliasedObjectSet.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//   Aaron Bockover <abockover 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.Text;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Hyena.Query
+{
+    public interface IAliasedObject
+    {
+        string Name { get; }
+        string [] Aliases { get; }
+    }
+
+    public class AliasedObjectSet<T> : IEnumerable<T> where T : IAliasedObject
+    {
+        protected Dictionary<string, T> map = new Dictionary<string, T> ();
+        protected List<string> aliases = new List<string> ();
+        protected T [] objects;
+
+        public AliasedObjectSet (params T [] objects)
+        {
+            this.objects = objects;
+            foreach (T obj in objects) {
+                map [obj.Name.ToLower ()] = obj;
+                foreach (string alias in obj.Aliases) {
+                    if (!String.IsNullOrEmpty (alias) && alias.IndexOf (" ") == -1) {
+                        string lower_alias = alias.ToLower ();
+                        map [lower_alias] = obj;
+                        if (!aliases.Contains (lower_alias)) {
+                            aliases.Add (lower_alias);
+                        }
+                    }
+                }
+            }
+            aliases.Sort (SortByLongest);
+        }
+
+        private int SortByLongest (string a, string b)
+        {
+            return b.Length.CompareTo (a.Length);
+        }
+
+        public string FindAlias (string input)
+        {
+            foreach (string alias in aliases) {
+                if (input.Contains (alias)) {
+                    return alias;
+                }
+            }
+            return null;
+        }
+
+        public T [] Objects {
+            get { return objects; }
+        }
+
+        public T First {
+            get { return objects [0]; }
+        }
+
+        public IEnumerator<T> GetEnumerator ()
+        {
+            foreach (T o in objects) {
+                yield return o;
+            }
+        }
+
+        IEnumerator IEnumerable.GetEnumerator ()
+        {
+            return GetEnumerator ();
+        }
+
+        public T this [string alias] {
+            get {
+                if (!String.IsNullOrEmpty (alias) && map.ContainsKey (alias.ToLower ()))
+                    return map[alias.ToLower ()];
+                return default(T);
+            }
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/DateQueryValue.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/DateQueryValue.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/DateQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -46,26 +46,43 @@
 
     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 override string XmlElementName {
             get { return "date"; }
         }
 
+        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (Equal, NotEqual, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual);
+        public override AliasedObjectSet<Operator> OperatorSet {
+            get { return operators; }
+        }
+
         public override object Value {
             get { return value; }
         }
 
         public bool Relative {
             get { return relative; }
-            set { relative = value; }
+            //set { relative = value; }
+        }
+
+        public RelativeDateFactor Factor {
+            get { return factor; }
         }
 
         public long RelativeOffset {
             get { return offset; }
-            set { offset = value; IsEmpty = false; }
+            //set { offset = value; IsEmpty = false; }
         }
 
         public override void ParseUserQuery (string input)
@@ -88,16 +105,38 @@
             }
         }
 
-        public override void ParseXml (XmlElement node)
+        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 (node.HasAttribute ("type") && node.GetAttribute ("type") == "rel") {
-                    relative = true;
-                    offset = Convert.ToInt64 (node.InnerText);
+                if (isRelative) {
+                    SetRelativeValue (Convert.ToInt64 (val), RelativeDateFactor.Second);
                 } else {
-                    value = DateTime.Parse (node.InnerText);
+                    SetValue (DateTime.Parse (val));
                 }
-                IsEmpty = false;
+            } catch {
+                IsEmpty = true;
+            }
+        }
+
+        public override void ParseXml (XmlElement node)
+        {
+            try {
+                LoadString (node.InnerText, node.HasAttribute ("type") && node.GetAttribute ("type") == "rel");
             } catch {
                 IsEmpty = true;
             }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/IntegerQueryValue.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/IntegerQueryValue.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/IntegerQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -36,6 +36,13 @@
 {
     public class IntegerQueryValue : 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 long value;
 
         public override string XmlElementName {
@@ -52,6 +59,22 @@
             ParseUserQuery (node.InnerText);
         }
 
+        public void SetValue (int value)
+        {
+            SetValue ((long) value);
+        }
+
+        public void SetValue (long value)
+        {
+            this.value = value;
+            IsEmpty = false;
+        }
+
+        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (Equal, NotEqual, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual);
+        public override AliasedObjectSet<Operator> OperatorSet {
+            get { return operators; }
+        }
+
         public override object Value {
             get { return value; }
         }
@@ -59,5 +82,22 @@
         public long IntValue {
             get { return value; }
         }
+
+        public virtual long DefaultValue {
+            get { return 0; }
+        }
+
+        public virtual long MinValue {
+            get { return Int64.MinValue; }
+        }
+
+        public virtual long MaxValue {
+            get { return Int64.MaxValue; }
+        }
+
+        public override string ToSql ()
+        {
+            return Convert.ToString (value);
+        }
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/QueryField.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/QueryField.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/QueryField.cs	Thu Feb  7 00:37:29 2008
@@ -33,7 +33,7 @@
 
 namespace Hyena.Query
 {
-    public class QueryField
+    public class QueryField : IAliasedObject
     {
         private Type value_type;
         public Type ValueType {
@@ -94,6 +94,13 @@
             this.value_type = valueType;
             this.is_default = isDefault;
             this.aliases = aliases;
+
+            QueryValue.AddValueType (value_type);
+        }
+
+        public QueryValue CreateQueryValue ()
+        {
+            return Activator.CreateInstance (ValueType) as QueryValue;
         }
 
         public string ToTermString (string op, string value)
@@ -101,21 +108,23 @@
             return ToTermString (PrimaryAlias, op, value);
         }
 
-        public string FormatSql (string format, Operator op, string value)
+        public string ToSql (Operator op, QueryValue qv)
         {
+            string value = qv.ToSql ();
+            if (op == null) op = qv.OperatorSet.First;
             if (Column.IndexOf ("{0}") == -1 && Column.IndexOf ("{1}") == -1) {
                 if (ValueType == typeof(StringQueryValue)) {
                     // Match string values literally and against a lower'd version 
                     return String.Format ("({0} {1} OR LOWER({0}) {2})", Column,
-                        String.Format (format, value),
-                        String.Format (format, value.ToLower ())
+                        String.Format (op.SqlFormat, value),
+                        String.Format (op.SqlFormat, value.ToLower ())
                     );
                 } else {
-                    return String.Format ("{0} {1}", Column, String.Format (format, value));
+                    return String.Format ("{0} {1}", Column, String.Format (op.SqlFormat, value));
                 }
             } else {
                 return String.Format (
-                    Column, String.Format (format, value),
+                    Column, String.Format (op.SqlFormat, value),
                     value, op.IsNot ? "NOT" : null
                 );
             }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/QueryFieldSet.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/QueryFieldSet.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/QueryFieldSet.cs	Thu Feb  7 00:37:29 2008
@@ -33,35 +33,14 @@
 
 namespace Hyena.Query
 {
-    public class QueryFieldSet
+    public class QueryFieldSet : AliasedObjectSet<QueryField>
     {
-        private Dictionary<string, QueryField> map = new Dictionary<string, QueryField> ();
-        private QueryField [] fields;
-
-        public QueryFieldSet (params QueryField [] fields)
+        public QueryFieldSet (params QueryField [] fields) : base (fields)
         {
-            this.fields = fields;
-            foreach (QueryField field in fields) {
-                map[field.Name.ToLower ()] = field;
-                foreach (string alias in field.Aliases)
-                    if (!String.IsNullOrEmpty (alias) && alias.IndexOf (" ") == -1)
-                        map[alias.ToLower ()] = field;
-            }
         }
 
         public QueryField [] Fields {
-            get { return fields; }
-        }
-
-        public QueryField GetByAlias (string alias)
-        {
-            if (!String.IsNullOrEmpty (alias) && map.ContainsKey (alias.ToLower ()))
-                return map[alias.ToLower ()];
-            return null;
-        }
-
-        public QueryField this [string alias] {
-            get { return GetByAlias (alias); }
+            get { return Objects; }
         }
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/QueryOperator.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/QueryOperator.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/QueryOperator.cs	Thu Feb  7 00:37:29 2008
@@ -34,79 +34,49 @@
 
 namespace Hyena.Query
 {
-    public class Operator
+    public class Operator : IAliasedObject
     {
-        public string Name;
-        public string UserOperator;
-
-        public Operator Dual {
-            get {
-                string op = UserOperator.Replace ('>', '^');
-                op = op.Replace ('<', '>');
-                op = op.Replace ('^', '<');
-                return GetByUserOperator (op);
-            }
-        }
-
-        private bool is_not;
-        public bool IsNot {
-            get { return is_not; }
+        public string name;
+        public string Name {
+            get { return name; }
         }
-        
-        private static List<Operator> operators = new List<Operator> ();
-        private static Dictionary<string, Operator> by_op = new Dictionary<string, Operator> ();
-        private static Dictionary<string, Operator> by_name = new Dictionary<string, Operator> ();
-
 
-        protected Operator (string name, string userOp) : this (name, userOp, false)
-        {
+        public string label;
+        public string Label {
+            get { return label; }
+            set { label = value; }
         }
 
-        protected Operator (string name, string userOp, bool is_not)
-        {
-            Name = name;
-            UserOperator = userOp;
-            this.is_not = is_not;
+        private string [] aliases;
+        public string [] Aliases {
+            get { return aliases; }
         }
 
-        static Operator () {
-            // Note, order of these is important since if = was before ==, the value of the
-            // term would start with the second =, etc.
-            Add (new Operator ("equals", "=="));
-            Add (new Operator ("lessThanEquals", "<="));
-            Add (new Operator ("greaterThanEquals", ">="));
-            Add (new Operator ("notEqual", "!=", true));
-            Add (new Operator ("endsWith", ":="));
-            Add (new Operator ("startsWith", "="));
-            Add (new Operator ("doesNotContain", "!:", true));
-            Add (new Operator ("contains", ":"));
-            Add (new Operator ("lessThan", "<"));
-            Add (new Operator ("greaterThan", ">"));
+        public string PrimaryAlias {
+            get { return aliases [0]; }
         }
 
-        public static IEnumerable<Operator> Operators {
-            get { return operators; }
+        private string sql_format;
+        public string SqlFormat {
+            get { return sql_format; }
         }
 
-        private static void Add (Operator op)
-        {
-            operators.Add (op);
-            by_op.Add (op.UserOperator, op);
-            by_name.Add (op.Name, op);
+        // FIXME get rid of this
+        private bool is_not;
+        public bool IsNot {
+            get { return is_not; }
         }
-
-        public static Operator GetByUserOperator (string op)
+        
+        internal Operator (string name, string sql_format, params string [] userOps) : this (name, sql_format, false, userOps)
         {
-            return (by_op.ContainsKey (op)) ? by_op [op] : null;
         }
 
-        public static Operator GetByName (string name)
+        internal Operator (string name, string sql_format, bool is_not, params string [] userOps)
         {
-            return (by_name.ContainsKey (name)) ? by_name [name] : null;
-        }
-
-        public static Operator Default {
-            get { return Operator.GetByUserOperator (":"); }
+            this.name = name;
+            this.sql_format = sql_format;
+            this.aliases = userOps;
+            this.is_not = is_not;
         }
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/QueryTermNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/QueryTermNode.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/QueryTermNode.cs	Thu Feb  7 00:37:29 2008
@@ -37,31 +37,35 @@
     public class QueryTermNode : QueryNode
     {
         private QueryField field;
-        private Operator op = Operator.Default;
+        private Operator op;
         private QueryValue qvalue;
 
         public static QueryTermNode ParseUserQuery (QueryFieldSet field_set, string token)
         {
-            QueryTermNode node = new QueryTermNode ();
-            int field_separator = 0;
-            foreach (Operator op in Operator.Operators) {
-                field_separator = token.IndexOf (op.UserOperator);
-                if (field_separator != -1) {
-                    node.Operator = op;
-                    break;
-                }
-            }
+            QueryTermNode term = new QueryTermNode ();
 
-            if (field_separator > 0) {
-                node.Field = field_set[token.Substring (0, field_separator)];
-                if (node.Field != null) {
-                    token = token.Substring (field_separator + node.Operator.UserOperator.Length);
+            // See if the query specifies a field, and if so, pull out the operator as well
+            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 {
+                    term.Value = null;
+                    term.Field = null;
                 }
             }
 
-            node.Value = QueryValue.CreateFromUserQuery (token, node.Field);
+            if (term.Value == null) {
+                term.Value = QueryValue.CreateFromUserQuery (token, term.Field);
+            }
 
-            return node;
+            return term;
         }
 
         public QueryTermNode () : base ()
@@ -70,19 +74,19 @@
 
         public override QueryNode Trim ()
         {
-            if ((qvalue == null || qvalue.IsEmpty) && Parent != null)
+            if (Parent != null && (qvalue == null || qvalue.IsEmpty || (field != null && op == null)))
                 Parent.RemoveChild (this);
             return this;
         }
         
         public override void AppendUserQuery (StringBuilder sb)
         {
-            sb.Append (Field == null ? Value.ToUserQuery () : Field.ToTermString (Operator.UserOperator, Value.ToUserQuery ()));
+            sb.Append (Field == null ? Value.ToUserQuery () : Field.ToTermString (Operator.PrimaryAlias, Value.ToUserQuery ()));
         }
 
         public override void AppendXml (XmlDocument doc, XmlNode parent, QueryFieldSet fieldSet)
         {
-            XmlElement op_node = doc.CreateElement (op.Name);
+            XmlElement op_node = doc.CreateElement (op == null ? "contains" : op.Name);
             parent.AppendChild (op_node);
 
             QueryField field = Field;
@@ -120,83 +124,14 @@
             if (Value.IsEmpty)
                 return false;
 
-            if (field.ValueType == typeof(StringQueryValue)) {
-                return EmitStringMatch (sb, field, emit_or);
-            } else {
-                return EmitNumericMatch (sb, field, emit_or);
-            }
-        }
-
-        private bool EmitStringMatch (StringBuilder sb, QueryField field, bool emit_or)
-        {
-            string safe_value = Value.ToSql ().Replace("'", "''");
-            
-            if (emit_or)
-                sb.Append (" OR ");
-
-            string format = null;
-            switch (Operator.UserOperator) {
-                case "=": // Starts with
-                    format = "LIKE '{0}%'";
-                    break;
-
-                case ":=": // Ends with
-                    format = "LIKE '%{0}'";
-                    break;
-                    
-                case "==": // Equal to
-                    format = "= '{0}'";
-                    break;
-
-                case "!=": // Not equal to
-                    format = "!= '{0}'";
-                    break;
-
-                case "!:": // Doesn't contain
-                    format = "NOT LIKE '%{0}%'";
-                    break;
-
-                case ":": // Contains
-                default:
-                    format = "LIKE '%{0}%'";
-                    break;
-            }
-
-            sb.Append (field.FormatSql (format, Operator, safe_value));
-            return true;
-        }
-
-        private bool EmitNumericMatch (StringBuilder sb, QueryField field, bool emit_or)
-        {
-            string format;
-            switch (Operator.UserOperator) {
-                // Operators that don't make sense for numeric types
-                case "!:":
-                case ":=":
-                    return false;
-
-                case ":":
-                case "=":
-                case "==":
-                    format = "= {0}";
-                    break;
-
-                default:
-                    format = Operator.UserOperator + " {0}";
-                    break;
-            }
-
             if (emit_or) {
                 sb.Append (" OR ");
             }
 
-            sb.Append (field.FormatSql (format,
-                Operator, Value.ToSql ()
-            ));
-
+            sb.Append (field.ToSql (Operator, Value));
             return true;
         }
-        
+
         public QueryField Field {
             get { return field; }
             set { field = value; }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/QueryValue.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/QueryValue.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/QueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -27,6 +27,7 @@
 //
 
 using System;
+using System.Collections.Generic;
 using System.Xml;
 using System.Text;
 
@@ -36,11 +37,17 @@
 {
     public abstract class QueryValue
     {
-        private static Type [] subtypes = new Type [] {typeof(StringQueryValue), typeof(IntegerQueryValue), typeof(FileSizeQueryValue), typeof(DateQueryValue)};
+        private static List<Type> subtypes = new List<Type> ();
+        public static void AddValueType (Type type)
+        {
+            if (!subtypes.Contains (type)) {
+                subtypes.Add (type);
+            }
+        }
 
         public static QueryValue CreateFromUserQuery (string input, QueryField field)
         {
-            QueryValue val = (field == null) ? new StringQueryValue () : Activator.CreateInstance (field.ValueType) as QueryValue;
+            QueryValue val = (field == null) ? new StringQueryValue () : field.CreateQueryValue ();
             val.ParseUserQuery (input);
             return val;
         }
@@ -48,7 +55,7 @@
         public static QueryValue CreateFromXml (XmlElement parent, QueryField field)
         {
             if (field != null) {
-                QueryValue val = Activator.CreateInstance (field.ValueType) as QueryValue;
+                QueryValue val = field.CreateQueryValue ();
                 return CreateFromXml (val, parent) ? val : null;
             } else {
                 foreach (Type subtype in subtypes) {
@@ -79,23 +86,22 @@
 
         public abstract object Value { get; }
         public abstract string XmlElementName { get; }
+        public abstract AliasedObjectSet<Operator> OperatorSet { get; }
+
+        public abstract void ParseXml (XmlElement node);
 
         public virtual void AppendXml (XmlElement node)
         {
             node.InnerText = Value.ToString ();
         }
 
-        public virtual string ToUserQuery ()
-        {
-            return Value.ToString ();
-        }
+        public abstract void ParseUserQuery (string input);
 
-        public virtual string ToSql ()
+        public virtual string ToUserQuery ()
         {
             return Value.ToString ();
         }
 
-        public abstract void ParseUserQuery (string input);
-        public abstract void ParseXml (XmlElement node);
+        public abstract string ToSql ();
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/StringQueryValue.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/StringQueryValue.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/StringQueryValue.cs	Thu Feb  7 00:37:29 2008
@@ -36,6 +36,13 @@
 {
     public class StringQueryValue : QueryValue
     {
+        public static readonly Operator Contains       = new Operator ("contains", "LIKE '%{0}%'", ":");
+        public static readonly Operator DoesNotContain = new Operator ("doesNotContain", "NOT LIKE '%{0}%'", true, "!:");
+        public static readonly Operator Equal          = new Operator ("equals", "= '{0}'", "==");
+        public static readonly Operator NotEqual       = new Operator ("notEqual", "!= '{0}'", true, "!=");
+        public static readonly Operator StartsWith     = new Operator ("startsWith", "LIKE '{0}%'", "=");
+        public static readonly Operator EndsWith       = new Operator ("endsWith", "LIKE '%{0}'", ":=");
+
         protected string value;
 
         public override string XmlElementName {
@@ -46,6 +53,11 @@
             get { return value; }
         }
 
+        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (Contains, DoesNotContain, Equal, NotEqual, StartsWith, EndsWith);
+        public override AliasedObjectSet<Operator> OperatorSet {
+            get { return operators; }
+        }
+
         public override void ParseUserQuery (string input)
         {
             value = input;
@@ -57,5 +69,16 @@
             value = node.InnerText;
             IsEmpty = String.IsNullOrEmpty (value);
         }
+
+        public void SetValue (string str)
+        {
+            value = str;
+            IsEmpty = String.IsNullOrEmpty (value);
+        }
+
+        public override string ToSql ()
+        {
+            return String.IsNullOrEmpty (value) ? null : value.Replace ("'", "''");
+        }
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/XmlQueryParser.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/XmlQueryParser.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/XmlQueryParser.cs	Thu Feb  7 00:37:29 2008
@@ -91,9 +91,6 @@
                 default:
                     QueryTermNode term = new QueryTermNode ();
 
-                    // Get the operator from the term's name
-                    term.Operator = Operator.GetByName (node.Name);
-
                     // Get the field (if any) that this term applies to
                     if (node["field"] != null)
                         term.Field = field_set [node["field"].GetAttribute ("name")];
@@ -101,6 +98,10 @@
                     // Get the value
                     term.Value = QueryValue.CreateFromXml (node, term.Field);
 
+                    // Get the operator from the term's name
+                    term.Operator = term.Value.OperatorSet [node.Name];
+
+
                     if (parent != null) {
                         parent.AddChild (term);
                     }

Modified: trunk/banshee/src/Core/Hyena/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Hyena/Makefile.am	(original)
+++ trunk/banshee/src/Core/Hyena/Makefile.am	Thu Feb  7 00:37:29 2008
@@ -32,6 +32,7 @@
 	Hyena.Data/ModelSelection.cs \
 	Hyena.Data/PropertyStore.cs \
 	Hyena.Data/SortType.cs \
+	Hyena.Query/AliasedObjectSet.cs \
 	Hyena.Query/DateQueryValue.cs \
 	Hyena.Query/FileSizeQueryValue.cs \
 	Hyena.Query/IntegerQueryValue.cs \



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