banshee r3213 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Database src/Core/Banshee.Services/Banshee.Query src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.ThickClient 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: Mon Feb 11 02:30:35 2008
New Revision: 3213
URL: http://svn.gnome.org/viewvc/banshee?rev=3213&view=rev

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

	This commit greatly improves the smart playlist editor, both in terms of
	coming up to par with what we have in stable, and in terms of doing that in
	a clean way.  You'll need to blow away your current trunk db to have your
	smart playlists remigrated.  Limits to smart playlists now are functional.
	You can create and edit smart playlits.

	* src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs:
	Move GetSort method to BansheeQuery to provide consistent sorting for
	smart playlists too.

	* src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs:
	Make LimitCriterion a TEXT field and get rid of OrderDir.  Database change,
	no migration, so run src/nuke-core-tables.

	* src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs: Add public
	static QueryOrder and QueryLimit arrays that define the types of sorting
	and limiting smart playlists can handle.  Break QueryFields into separate
	public static variables in addition to providing FieldSet.  Within static
	constructor, translate Hyena query operators.

	* src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs:
	Inherit from PlaylistQueryValue.

	* src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs: Get rid of
	OrderDir, migrate old smart playlists to use new QueryLimit/Order classes.

	* src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs:
	Add QueryOrder, Limit, and LimitValue properties that, if all set, define
	how a smart playlist is limited.  Move list of Orders to BansheeQuery.

	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs: Add
	SmartPlaylistQueryValueEntry mapping, and instantiate base class
	(QueryBox) with BansheeQuery's FieldSet, Orders, and Limits.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs:
	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs:
	Specify a fixed widths for value entries.

	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs:
	Exact same thing as PlaylistQueryValueEntry - should probably be merged or
	factored out.

	* src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs: Update
	to work with QueryLimit/Order classes and save/restore the limit/order.
	Update the predefined playlists, though I think they're still broken atm.

	* src/Core/Banshee.ThickClient/Makefile.am: Add new class.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs: Make sure
	query value and gui are in sync, and set a default combo box value.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/FileSizeQueryValueEntry.cs: Similar
	to date value entry, provide a dropdown with MB, GB, etc.

	* src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs:
	* src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs: Make sure
	the query value is in sync with the gui.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs: Contains a QueryTermsBox
	and a QueryLimitBox.  Provides the main GUI interface that the smart
	playlist Editor deals with.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryLimitBox.cs: New class, a HBox
	that contains widgetry to limit a smart playlist.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs: Instead of defining
	this as a HBox with field, op, value across it, make QueryTermBox not
	actually a widet, and put its components into their respective VBoxes so
	that each column is always the same width.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs: New file, a HBox
	containing VBoxes for field, op, value, and button columns filled by the
	components of one or more QueryTermBoxes.

	* src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs: Set spacing,
	define default width for entries, and add FileSize entry mapping.

	* src/Core/Hyena.Gui/Makefile.am: Add new classes.

	* src/Core/Hyena/Hyena.Query/DateQueryValue.cs: Only offer GreaterThan and
	LessThan operators, for now at least.

	* src/Core/Hyena/Hyena.Query/FileSizeQueryValue.cs: Define SetValue
	method.

	* src/Core/Hyena/Hyena.Query/QueryLimit.cs: New class used to create
	named, labelized limit criteria (row based and not).  Smart playlists use
	this to LIMIT their membership if needed.  Current, non row-based Limits
	aren't implemented.

	* src/Core/Hyena/Hyena.Query/QueryOrder.cs: New class used to create a
	list of named, labelized orderings.  Used in Banshee to give a list of
	orderings ("selected by") when you limit a smart playlist.

	* src/Core/Hyena/Hyena.Query/QueryValue.cs: Add ToString override.

	* src/Core/Hyena/Makefile.am: New files.


Added:
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/FileSizeQueryValueEntry.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryLimitBox.cs
   trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryLimit.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryOrder.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.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.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/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am
   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.Gui/Makefile.am
   trunk/banshee/src/Core/Hyena/Hyena.Query/DateQueryValue.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/FileSizeQueryValue.cs
   trunk/banshee/src/Core/Hyena/Hyena.Query/QueryValue.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	Mon Feb 11 02:30:35 2008
@@ -112,77 +112,11 @@
             }
         }
 
-        private string AscDesc ()
-        {
-            return sort_column.SortType == SortType.Ascending ? " ASC" : " DESC";
-        }
-
         private void GenerateSortQueryPart()
         {
-            if(sort_column == null) {
-                sort_query = null;
-                return;
-            }
-            
-            sort_query = GetSort (sort_column.SortKey, AscDesc ());
-        }
-
-        private const string default_sort = "lower(CoreArtists.Name) ASC, lower(CoreAlbums.Title) ASC, CoreTracks.Disc ASC, CoreTracks.TrackNumber ASC, CoreTracks.Uri ASC";
-        public static string GetSort (string key, string ascDesc)
-        {
-            string sort_query = null;
-            switch(key) {
-                case "Track":
-                    sort_query = String.Format (@"
-                        lower(CoreArtists.Name) ASC, 
-                        lower(CoreAlbums.Title) ASC, 
-                        CoreTracks.TrackNumber {0}", ascDesc); 
-                    break;
-
-                case "Artist":
-                    sort_query = String.Format (@"
-                        lower(CoreArtists.Name) {0}, 
-                        lower(CoreAlbums.Title) ASC,
-                        CoreTracks.Disc ASC,
-                        CoreTracks.TrackNumber ASC,
-                        CoreTracks.Uri ASC", ascDesc); 
-                    break;
-
-                case "Album":
-                    sort_query = String.Format (@"
-                        lower(CoreAlbums.Title) {0},
-                        CoreTracks.Disc ASC,
-                        CoreTracks.TrackNumber ASC,
-                        CoreTracks.Uri ASC", ascDesc); 
-                    break;
-
-                case "Title":
-                    sort_query = String.Format (@"
-                        lower(CoreTracks.Title) {0},
-                        lower(CoreArtists.Name) ASC, 
-                        lower(CoreAlbums.Title) ASC", ascDesc); 
-                    break;
-
-                case "Random":
-                    sort_query = "RANDOM ()";
-                    break;
-
-                case "Year":
-                case "Disc":
-                case "Duration":
-                case "Rating":
-                case "PlayCount":
-                case "SkipCount":
-                case "LastPlayedStamp":
-                case "DateAddedStamp":
-                case "Uri":
-                    sort_query = String.Format (
-                        "CoreTracks.{0} {1}, {2}",
-                        key, ascDesc, default_sort
-                    );
-                    break;
-            }
-            return sort_query;
+            sort_query = (sort_column == null) ?
+                null :
+                BansheeQuery.GetSort (sort_column.SortKey, sort_column.SortType == SortType.Ascending);
         }
 
         public void Refilter()

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	Mon Feb 11 02:30:35 2008
@@ -337,9 +337,8 @@
                     Name                TEXT NOT NULL,
                     Condition           TEXT,
                     OrderBy             TEXT,
-                    OrderDir            TEXT,
                     LimitNumber         TEXT,
-                    LimitCriterion      INTEGER
+                    LimitCriterion      TEXT
                 )
             ");
                 

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs	Mon Feb 11 02:30:35 2008
@@ -40,113 +40,273 @@
 {
     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"
-            )
+        private static bool asc = true;
+        private static bool desc = false;
+        public static QueryOrder [] Orders = new QueryOrder [] {
+            CreateQueryOrder ("Random",     asc,  Catalog.GetString ("Random")),
+            CreateQueryOrder ("Album",      asc,  Catalog.GetString ("Album")),
+            CreateQueryOrder ("Artist",     asc,  Catalog.GetString ("Artist")),
+            CreateQueryOrder ("Title",      asc,  Catalog.GetString ("Title")),
+            null,
+            CreateQueryOrder ("Rating",     desc, Catalog.GetString ("Highest Rating")),
+            CreateQueryOrder ("Rating",     asc,  Catalog.GetString ("Lowest Rating")),
+            null,
+            CreateQueryOrder ("PlayCount",  desc, Catalog.GetString ("Most Often Played")),
+            CreateQueryOrder ("PlayCount",  asc,  Catalog.GetString ("Least Often Played")),
+            null,
+            CreateQueryOrder ("LastPlayed", desc, Catalog.GetString ("Most Recently Played")),
+            CreateQueryOrder ("LastPlayed", asc,  Catalog.GetString ("Least Recently Played")),
+            null,
+            CreateQueryOrder ("DateAdded",  desc, Catalog.GetString ("Most Recently Added")),
+            CreateQueryOrder ("DateAdded",  asc,  Catalog.GetString ("Least Recently Added"))
+        };
+
+        public static QueryLimit [] Limits = new QueryLimit [] {
+            new QueryLimit ("songs",   Catalog.GetString("songs"), true),
+            new QueryLimit ("minutes", Catalog.GetString("minutes"), "CoreTracks.Duration", (int) RelativeDateFactor.Minute),
+            new QueryLimit ("hours",   Catalog.GetString("hours"), "CoreTracks.Duration", (int) RelativeDateFactor.Hour),
+            new QueryLimit ("MB",      Catalog.GetString("MB"), "CoreTracks.FileSize", (int) FileSizeFactor.MB),
+            new QueryLimit ("GB",      Catalog.GetString("GB"), "CoreTracks.FileSize", (int) FileSizeFactor.GB)
+        };
+
+        public static QueryField ArtistField = 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"
+        );
+
+        public static QueryField AlbumField = 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"
+        );
+
+        public static QueryField DiscField = 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"
+        );
+
+        public static QueryField TitleField = 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"
+        );
+
+        public static QueryField YearField = 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"
+        );
+
+        public static QueryField RatingField = 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"
+        );
+
+        public static QueryField PlayCountField = 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"
         );
+
+        public static QueryField SkipCountField = 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"
+        );
+
+        public static QueryField FileSizeField = 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"
+        );
+
+        public static QueryField UriField = 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"
+        );
+
+        public static QueryField DurationField = 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"
+        );
+
+        public static QueryField MimeTypeField = 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"
+        );
+
+        public static QueryField LastPlayedField = 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"
+        );
+
+        public static QueryField DateAddedField = 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"
+        );
+
+        public static QueryField PlaylistField = new QueryField (
+            "playlistid", Catalog.GetString ("Playlist"),
+            "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CorePlaylistEntries WHERE PlaylistID = {1})", typeof(PlaylistQueryValue),
+            "playlistid", "playlist"
+        );
+
+        public static QueryField SmartPlaylistField = new QueryField (
+            "smartplaylistid", Catalog.GetString ("Smart Playlist"),
+            "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CoreSmartPlaylistEntries WHERE SmartPlaylistID = {1})", typeof(SmartPlaylistQueryValue),
+            "smartplaylistid", "smartplaylist"
+        );
+
+        public static QueryFieldSet FieldSet = new QueryFieldSet (
+            ArtistField, AlbumField, DiscField, TitleField, YearField, RatingField, PlayCountField,
+            SkipCountField, FileSizeField, UriField, DurationField, MimeTypeField, LastPlayedField,
+            DateAddedField, PlaylistField, SmartPlaylistField
+        );
+
+        private const string default_sort = "lower(CoreArtists.Name) ASC, lower(CoreAlbums.Title) ASC, CoreTracks.Disc ASC, CoreTracks.TrackNumber ASC, CoreTracks.Uri ASC";
+
+        public static string GetSort (string key)
+        {
+            return GetSort (key, false);
+        }
+
+        public static string GetSort (string key, bool asc)
+        {
+            string ascDesc = asc ? "ASC" : "DESC";
+            string sort_query = null;
+            switch(key) {
+                case "Track":
+                    sort_query = String.Format (@"
+                        lower(CoreArtists.Name) ASC, 
+                        lower(CoreAlbums.Title) ASC, 
+                        CoreTracks.TrackNumber {0}", ascDesc); 
+                    break;
+
+                case "Artist":
+                    sort_query = String.Format (@"
+                        lower(CoreArtists.Name) {0}, 
+                        lower(CoreAlbums.Title) ASC,
+                        CoreTracks.Disc ASC,
+                        CoreTracks.TrackNumber ASC,
+                        CoreTracks.Uri ASC", ascDesc); 
+                    break;
+
+                case "Album":
+                    sort_query = String.Format (@"
+                        lower(CoreAlbums.Title) {0},
+                        CoreTracks.Disc ASC,
+                        CoreTracks.TrackNumber ASC,
+                        CoreTracks.Uri ASC", ascDesc); 
+                    break;
+
+                case "Title":
+                    sort_query = String.Format (@"
+                        lower(CoreTracks.Title) {0},
+                        lower(CoreArtists.Name) ASC, 
+                        lower(CoreAlbums.Title) ASC", ascDesc); 
+                    break;
+
+                case "Random":
+                    sort_query = "RANDOM ()";
+                    break;
+
+                case "Year":
+                case "Disc":
+                case "Duration":
+                case "Rating":
+                case "PlayCount":
+                case "SkipCount":
+                case "LastPlayedStamp":
+                case "DateAddedStamp":
+                case "Uri":
+                    sort_query = String.Format (
+                        "CoreTracks.{0} {1}, {2}",
+                        key, ascDesc, default_sort
+                    );
+                    break;
+            }
+            return sort_query;
+        }
+
+        private static QueryOrder CreateQueryOrder (string name, bool asc, string label)
+        {
+            return new QueryOrder (CreateOrderName (name, asc), label, GetSort (name, asc));
+        }
+
+        public static QueryLimit FindLimit (string name)
+        {
+            foreach (QueryLimit limit in Limits) {
+                if (limit.Name == name)
+                    return limit;
+            }
+            return null;
+        }
+
+        public static QueryOrder FindOrder (string name, bool asc)
+        {
+            return FindOrder (CreateOrderName (name, asc));
+        }
+
+        public static QueryOrder FindOrder (string name)
+        {
+            Console.WriteLine ("# ordres = {0}", Orders.Length);
+            foreach (QueryOrder order in Orders) {
+                if (order != null && order.Name == name) {
+                    return order;
+                }
+            }
+            return null;
+        }
+
+        private static string CreateOrderName (string name, bool asc)
+        {
+            return String.Format ("{0}-{1}", name, asc ? "ASC" : "DESC");
+        }
+
+        static BansheeQuery () {
+            // Set translated names for operators
+            IntegerQueryValue.Equal.Label            = Catalog.GetString ("is");
+            IntegerQueryValue.NotEqual.Label         = Catalog.GetString ("is not");
+            IntegerQueryValue.LessThanEqual.Label    = Catalog.GetString ("at most");
+            IntegerQueryValue.GreaterThanEqual.Label = Catalog.GetString ("at least");
+            IntegerQueryValue.LessThan.Label         = Catalog.GetString ("less than");
+            IntegerQueryValue.GreaterThan.Label      = Catalog.GetString ("more than");
+
+            DateQueryValue.Equal.Label               = Catalog.GetString ("is");
+            DateQueryValue.NotEqual.Label            = Catalog.GetString ("is not");
+            DateQueryValue.LessThanEqual.Label       = Catalog.GetString ("at most");
+            DateQueryValue.GreaterThanEqual.Label    = Catalog.GetString ("at least");
+            DateQueryValue.LessThan.Label            = Catalog.GetString ("less than");
+            DateQueryValue.GreaterThan.Label         = Catalog.GetString ("more than");
+
+            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");
+        }
     }
 }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Query/SmartPlaylistQueryValue.cs	Mon Feb 11 02:30:35 2008
@@ -31,7 +31,7 @@
 
 namespace Banshee.Query
 {
-    public class SmartPlaylistQueryValue : IntegerQueryValue
+    public class SmartPlaylistQueryValue : PlaylistQueryValue
     {
     }
 }

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	Mon Feb 11 02:30:35 2008
@@ -45,8 +45,8 @@
 {
     internal class Migrator
     {
-        private string [] criteria = new string [] { "tracks", "minutes", "hours", "MB" };
-        private Dictionary<string, Order> order_hash = new Dictionary<string, Order> ();
+        private string [] criteria = new string [] { "songs", "minutes", "hours", "MB" };
+        private Dictionary<string, QueryOrder> order_hash = new Dictionary<string, QueryOrder> ();
 
         public static void MigrateAll ()
         {
@@ -56,13 +56,12 @@
 
             Migrator m = new Migrator ();
             using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (
-                "SELECT SmartPlaylistID, Name, Condition, OrderBy, OrderDir, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
+                "SELECT SmartPlaylistID, Name, Condition, OrderBy, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
                 while (reader.Read ()) {
                     m.Migrate (
                         Convert.ToInt32 (reader[0]), reader[1] as string,
                         reader[2] as string, reader[3] as string,
-                        reader[4] as string, reader[5] as string,
-                        reader[6] as string
+                        reader[4] as string, reader[5] as string
                     );
                 }
             }
@@ -72,27 +71,27 @@
 
         public Migrator ()
         {
-            order_hash.Add ("RANDOM()", FindOrder ("Random"));
-            order_hash.Add ("AlbumTitle", FindOrder ("Album"));
-            order_hash.Add ("Artist", FindOrder ("Artist"));
-            order_hash.Add ("Genre", FindOrder ("Genre"));
-            order_hash.Add ("Title", FindOrder ("Title"));
-            order_hash.Add ("Rating DESC", FindOrder ("Rating", "DESC"));
-            order_hash.Add ("Rating ASC", FindOrder ("Rating", "ASC"));
-            order_hash.Add ("NumerOfPlays DESC", FindOrder ("PlayCount", "DESC"));
-            order_hash.Add ("NumerOfPlays ASC", FindOrder ("PlayCount", "ASC"));
-            order_hash.Add ("DateAddedStamp DESC", FindOrder ("DateAddedStamp", "DESC"));
-            order_hash.Add ("DateAddedStamp ASC", FindOrder ("DateAddedStamp", "ASC"));
-            order_hash.Add ("LastPlayedStamp DESC", FindOrder ("LastPlayedStamp", "DESC"));
-            order_hash.Add ("LastPlayedStamp ASC", FindOrder ("LastPlayedStamp", "ASC"));
+            order_hash.Add ("RANDOM()",             BansheeQuery.FindOrder ("Random", true));
+            order_hash.Add ("AlbumTitle",           BansheeQuery.FindOrder ("Album", true));
+            order_hash.Add ("Artist",               BansheeQuery.FindOrder ("Artist", true));
+            order_hash.Add ("Genre",                BansheeQuery.FindOrder ("Genre", true));
+            order_hash.Add ("Title",                BansheeQuery.FindOrder ("Title", true));
+            order_hash.Add ("Rating DESC",          BansheeQuery.FindOrder ("Rating", false));
+            order_hash.Add ("Rating ASC",           BansheeQuery.FindOrder ("Rating", true));
+            order_hash.Add ("NumberOfPlays DESC",   BansheeQuery.FindOrder ("PlayCount", false));
+            order_hash.Add ("NumberOfPlays ASC",    BansheeQuery.FindOrder ("PlayCount", true));
+            order_hash.Add ("DateAddedStamp DESC",  BansheeQuery.FindOrder ("DateAddedStamp", false));
+            order_hash.Add ("DateAddedStamp ASC",   BansheeQuery.FindOrder ("DateAddedStamp", true));
+            order_hash.Add ("LastPlayedStamp DESC", BansheeQuery.FindOrder ("LastPlayedStamp", false));
+            order_hash.Add ("LastPlayedStamp ASC",  BansheeQuery.FindOrder ("LastPlayedStamp", true));
         }
 
-        private void Migrate (int dbid, string Name, string Condition, string OrderBy, string OrderDir, string LimitNumber, string LimitCriterion)
+        private void Migrate (int dbid, string Name, string Condition, string OrderBy, string LimitNumber, string LimitCriterion)
         {
+            Console.WriteLine ("migrating {0}, cond = {1}, order = {2}", Name, Condition, OrderBy);
             if (OrderBy != null && OrderBy != String.Empty) {
-                Order order = order_hash [OrderBy];
-                OrderBy = order.Key;
-                OrderDir = order.Dir;
+                QueryOrder order = order_hash [OrderBy];
+                OrderBy = order.Name;
             }
 
             LimitCriterion = criteria [Convert.ToInt32 (LimitCriterion)];
@@ -103,12 +102,12 @@
                     SET Name = ?,
                         Condition = ?,
                         OrderBy = ?,
-                        OrderDir = ?,
                         LimitNumber = ?,
                         LimitCriterion = ?
                     WHERE SmartPlaylistID = ?",
-                Name, ConditionXml, OrderBy, OrderDir, LimitNumber, LimitCriterion, dbid
+                Name, ConditionXml, OrderBy, LimitNumber, LimitCriterion, dbid
             ));
+            Console.WriteLine ("migrated {0}, cond = {1}, order = {2}", Name, ConditionXml, OrderBy);
         }
 
         private string ParseCondition (string value)
@@ -228,20 +227,6 @@
             term.Value = date_value;
         }
 
-        private Order FindOrder (string key)
-        {
-            return FindOrder (key, null);
-        }
-
-        private Order FindOrder (string key, string dir)
-        {
-            foreach (Order o in SmartPlaylistSource.Orders) {
-                if (o.Key == key && (dir == null || o.Dir == dir))
-                    return o;
-            }
-            return default(Order);
-        }
-
         public sealed class QueryOperator
         {
             public string NewOp;

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	Mon Feb 11 02:30:35 2008
@@ -48,27 +48,14 @@
 
 namespace Banshee.SmartPlaylist
 {
-    public struct Order
-    {
-        public string Label, Key, Dir;
-
-        public Order (string key, string label, string dir)
-        {
-            Key = key;
-            Label = label;
-            Dir = dir;
-        }
-    }
-
     public class SmartPlaylistSource : AbstractPlaylistSource, IUnmapableSource
     {
         private static string generic_name = Catalog.GetString ("Smart Playlist");
         private static string properties_label = Catalog.GetString ("Edit Smart Playlist");
     
-        private string order_by;
-        private string order_dir;
-        private string limit_number;
-        private string limit_criterion;
+        private QueryOrder query_order;
+        private QueryLimit limit;
+        private IntegerQueryValue limit_value;
 
 #region Properties
 
@@ -121,46 +108,43 @@
             }
         }
 
-        public string OrderBy {
-            get { return order_by; }
-            set { order_by = value; }
+        public QueryOrder QueryOrder {
+            get { return query_order; }
+            set { query_order = value; }
         }
 
-        public string OrderDir {
-            get { return order_dir; }
-            set { order_dir = value; }
+        public IntegerQueryValue LimitValue {
+            get { return limit_value; }
+            set { limit_value = value; }
         }
 
-        public string Sort {
-            get { return TrackListDatabaseModel.GetSort (OrderBy, OrderDir); }
+        public QueryLimit Limit {
+            get { return limit; }
+            set { limit = value; }
         }
 
-        public string LimitNumber {
-            get { return limit_number; }
-            set { limit_number = value; }
-        }
-
-        public string LimitCriterion {
-            get { return limit_criterion; }
-            set { limit_criterion = value; }
-        }
-
-        private string OrderAndLimit {
+        protected string OrderAndLimit {
             get {
-                if (OrderBy == null || OrderBy == String.Empty)
+                if (IsLimited) {
+                    return String.Format ("{0} {1}", QueryOrder.ToSql (), Limit.ToSql (LimitValue));
+                } else {
                     return null;
+                }
+            }
+        }
 
-                if (LimitCriterion == null || LimitCriterion == String.Empty)
-                    return String.Format ("ORDER BY {0} LIMIT {1}", Sort, LimitNumber);
-                else
-                    return String.Format ("ORDER BY {0}", Sort);
+        public bool IsLimited {
+            get {
+                return (Limit != null && LimitValue != null && !LimitValue.IsEmpty && QueryOrder != null);
             }
         }
 
+        // FIXME scan ConditionTree for playlist fields
         public bool PlaylistDependent {
             get { return false; }
         }
 
+        // FIXME scan ConditionTree for date fields
         public bool TimeDependent {
             get { return false; }
         }
@@ -169,33 +153,38 @@
 
 #region Constructors
 
-        public SmartPlaylistSource (string name) : this (null, name, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty)
+        public SmartPlaylistSource (string name) : this (null, name, String.Empty, String.Empty, String.Empty, String.Empty)
         {
         }
 
-        /*public SmartPlaylistSource (SmartPlaylistSource original) : this (original.Name)
+        public SmartPlaylistSource (string name, QueryNode condition, QueryOrder order, QueryLimit limit, IntegerQueryValue limit_value)
+            : base (generic_name, name, null, -1, 0)
         {
-            ConditionXml = original.ConditionXml;
-            OrderBy = original.OrderBy;
-            OrderDir = original.OrderDir;
-            LimitNumber = original.LimitNumber;
-            LimitCriterion = original.LimitCriterion;
-        }*/
+            ConditionTree = condition;
+            QueryOrder = order;
+            Limit = limit;
+            LimitValue = limit_value;
+
+            InstallProperties ();
+        }
 
         // For existing smart playlists that we're loading from the database
-        public SmartPlaylistSource (int? dbid, string name, string condition_xml, string order_by, string order_dir, string limit_number, string limit_criterion) :
+        public SmartPlaylistSource (int? dbid, string name, string condition_xml, string order_by, string limit_number, string limit_criterion) :
             base (generic_name, name, dbid, -1, 0)
         {
             ConditionXml = condition_xml;
-            OrderBy = order_by;
-            OrderDir = order_dir;
-            LimitNumber = limit_number;
-            LimitCriterion = limit_criterion;
+            QueryOrder = BansheeQuery.FindOrder (order_by);
+            
+            Limit = BansheeQuery.FindLimit (limit_criterion);
+
+            LimitValue = new IntegerQueryValue ();
+            LimitValue.ParseUserQuery (limit_number);
+
+            Console.WriteLine ("limit = {0}, order = {1}, val = {2}, valisempty? {3}", Limit, QueryOrder, LimitValue, LimitValue.IsEmpty);
+
             DbId = dbid;
 
-            Properties.SetString ("IconName", "source-smart-playlist");
-            Properties.SetString ("SourcePropertiesActionLabel", properties_label);
-            Properties.SetString ("UnmapSourceActionLabel", Catalog.GetString ("Delete Smart Playlist"));
+            InstallProperties ();
 
             //Globals.Library.TrackRemoved += OnLibraryTrackRemoved;
 
@@ -207,6 +196,13 @@
             //ListenToPlaylists();
         }
 
+        protected void InstallProperties ()
+        {
+            Properties.SetString ("IconName", "source-smart-playlist");
+            Properties.SetString ("SourcePropertiesActionLabel", properties_label);
+            Properties.SetString ("UnmapSourceActionLabel", Catalog.GetString ("Delete Smart Playlist"));
+        }
+
 #endregion
 
 #region Public Methods
@@ -220,7 +216,6 @@
             return false;
         }
 
-
 #endregion
 
 #region AbstractPlaylist overrides
@@ -229,9 +224,12 @@
         {
             DbId = ServiceManager.DbConnection.Execute (new HyenaSqliteCommand (@"
                 INSERT INTO CoreSmartPlaylists
-                    (Name, Condition, OrderBy, OrderDir, LimitNumber, LimitCriterion)
-                    VALUES (?, ?, ?, ?, ?, ?)",
-                Name, ConditionXml, OrderBy, OrderDir, LimitNumber, LimitCriterion
+                    (Name, Condition, OrderBy, LimitNumber, LimitCriterion)
+                    VALUES (?, ?, ?, ?, ?)",
+                Name, ConditionXml,
+                IsLimited ? QueryOrder.Name : null,
+                IsLimited ? LimitValue.ToSql () : null,
+                IsLimited ? Limit.Name : null
             ));
         }
 
@@ -242,11 +240,14 @@
                     SET Name = ?,
                         Condition = ?,
                         OrderBy = ?,
-                        OrderDir = ?,
                         LimitNumber = ?,
                         LimitCriterion = ?
                     WHERE SmartPlaylistID = ?",
-                Name, ConditionXml, OrderBy, OrderDir, LimitNumber, LimitCriterion, DbId
+                Name, ConditionXml,
+                IsLimited ? QueryOrder.Name : null,
+                IsLimited ? LimitValue.ToSql () : null,
+                IsLimited ? Limit.Name : null,
+                DbId
             ));
         }
 
@@ -261,6 +262,17 @@
                 "DELETE FROM CoreSmartPlaylistEntries WHERE SmartPlaylistID = {0}", DbId
             ));
 
+            Console.WriteLine ("limited? {0}", IsLimited);
+
+            Console.WriteLine (String.Format (
+                @"INSERT INTO CoreSmartPlaylistEntries 
+                    SELECT {0} as SmartPlaylistID, TrackId
+                        FROM CoreTracks, CoreArtists, CoreAlbums
+                        WHERE CoreTracks.ArtistID = CoreArtists.ArtistID AND CoreTracks.AlbumID = CoreAlbums.AlbumID
+                        {1} {2}",
+                DbId, PrependCondition("AND"), OrderAndLimit
+            ));
+
             // Repopulate it 
             ServiceManager.DbConnection.Execute (String.Format (
                 @"INSERT INTO CoreSmartPlaylistEntries 
@@ -309,43 +321,18 @@
             return String.IsNullOrEmpty (ConditionSql) ? " " : String.Format ("{0} ({1})", with, ConditionSql);
         }
 
-        private static Order [] orders = new Order [] {
-            new Order ("Random",    Catalog.GetString("Random"), null),
-            new Order ("Album",     Catalog.GetString("Album"), "ASC"),
-            new Order ("Artist",    Catalog.GetString("Artist"), "ASC"),
-            new Order ("Genre",     Catalog.GetString("Genre"), "ASC"),
-            new Order ("Title",     Catalog.GetString("Title"), "ASC"),
-            //--
-            new Order ("Rating",    Catalog.GetString("Highest Rating"), "DESC"),
-            new Order ("Rating",    Catalog.GetString("Lowest Rating"), "ASC"),
-            //--
-            new Order ("PlayCount", Catalog.GetString("Least Played"), "ASC"),
-            new Order ("PlayCount", Catalog.GetString("Most Played"), "DESC"),
-            //--
-            new Order ("DateAddedStamp", Catalog.GetString("Most Recently Added"), "DESC"),
-            new Order ("DateAddedStamp", Catalog.GetString("Least Recently Added"), "ASC"),
-            //--
-            new Order ("LastPlayedStamp", Catalog.GetString("Most Recently Played"), "DESC"),
-            new Order ("LastPlayedStamp", Catalog.GetString("Least Recently Played"), "ASC")
-        };
-
-        public static Order [] Orders {
-            get { return orders; }
-        }
-
         public static List<SmartPlaylistSource> LoadAll ()
         {
             List<SmartPlaylistSource> sources = new List<SmartPlaylistSource> ();
 
             using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (
-                "SELECT SmartPlaylistID, Name, Condition, OrderBy, OrderDir, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
+                "SELECT SmartPlaylistID, Name, Condition, OrderBy, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
                 while (reader.Read ()) {
                     try {
                         SmartPlaylistSource playlist = new SmartPlaylistSource (
                             Convert.ToInt32 (reader[0]), reader[1] as string,
                             reader[2] as string, reader[3] as string,
-                            reader[4] as string, reader[5] as string,
-                            reader[6] as string
+                            reader[4] as string, reader[5] as string
                         );
                         sources.Add (playlist);
                     } catch (Exception e) {

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/BansheeQueryBox.cs	Mon Feb 11 02:30:35 2008
@@ -27,6 +27,7 @@
 //
 
 using Mono.Unix;
+using Gtk;
 
 using Hyena.Query;
 using Hyena.Query.Gui;
@@ -37,7 +38,7 @@
 {
     public class BansheeQueryBox : QueryBox
     {
-        public BansheeQueryBox () : base (BansheeQuery.FieldSet)
+        public BansheeQueryBox () : base (BansheeQuery.FieldSet, BansheeQuery.Orders, BansheeQuery.Limits)
         {
         }
 
@@ -45,28 +46,7 @@
             // 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");
+            QueryValueEntry.AddSubType (typeof(SmartPlaylistQueryValueEntry), typeof(SmartPlaylistQueryValue));
         }
     }
 }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/PlaylistQueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -51,6 +51,7 @@
         public PlaylistQueryValueEntry () : base ()
         {
             combo = ComboBox.NewText();
+            combo.WidthRequest = DefaultWidth;
 
             int count = 0;
             PlaylistSource playlist;
@@ -67,7 +68,6 @@
             combo.Active = 0;
 
             combo.Changed += HandleValueChanged;
-
         }
 
         public override QueryValue QueryValue {
@@ -80,6 +80,7 @@
                         combo.Active = playlist_id_combo_map [(int)query_value.IntValue];
                     } catch {}
                 }
+                query_value.SetValue (combo_playlist_id_map [combo.Active]);
                 combo.Changed += HandleValueChanged;
             }
         }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -56,6 +56,7 @@
                 entry.Changed -= HandleValueChanged;
                 query_value = value as RatingQueryValue;
                 entry.Value = (int) (query_value.IsEmpty ? query_value.DefaultValue : query_value.IntValue);
+                query_value.SetValue (entry.Value);
                 entry.Changed += HandleValueChanged;
             }
         }

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -0,0 +1,94 @@
+//
+// SmartPlaylistQueryValueEntry.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.SmartPlaylist;
+using Banshee.Widgets;
+using Banshee.Query;
+
+namespace Banshee.Query.Gui
+{
+    public class SmartPlaylistQueryValueEntry : QueryValueEntry
+    {
+        protected ComboBox combo;
+        protected SmartPlaylistQueryValue 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 SmartPlaylistQueryValueEntry () : base ()
+        {
+            combo = ComboBox.NewText();
+            combo.WidthRequest = DefaultWidth;
+
+            int count = 0;
+            SmartPlaylistSource playlist;
+            foreach (Source child in ServiceManager.SourceManager.DefaultSource.Children) {
+                playlist = child as SmartPlaylistSource;
+                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 SmartPlaylistQueryValue;
+                if (!query_value.IsEmpty) {
+                    try {
+                        combo.Active = playlist_id_combo_map [(int)query_value.IntValue];
+                    } catch {}
+                }
+                query_value.SetValue (combo_playlist_id_map [combo.Active]);
+                combo.Changed += HandleValueChanged;
+            }
+        }
+
+        protected void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetValue (combo_playlist_id_map [combo.Active]);
+        }
+    }
+}

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	Mon Feb 11 02:30:35 2008
@@ -10,6 +10,7 @@
  
 using Banshee.Base;
 using Banshee.Query;
+using Banshee.ServiceStack;
 using Banshee.Widgets;
 using Banshee.Sources;
 using Banshee.Database;
@@ -21,7 +22,6 @@
     public class Editor : GladeDialog
     {
         private BansheeQueryBox builder;
-        //private TracksQueryModel model;
 
         private SmartPlaylistSource playlist = null;
 
@@ -32,55 +32,50 @@
         [Widget] private Gtk.Button adv_use_button;
         [Widget] private Gtk.Button adv_add_button;
 
-        public Editor (SmartPlaylistSource playlist) : base("SmartPlaylistEditorDialog")
+        public Editor (SmartPlaylistSource playlist) : base ("SmartPlaylistEditorDialog")
         {
             this.playlist = playlist;
             Console.WriteLine ("Loading smart playlist into editor: {0}",
                 playlist.ConditionTree == null ? "" : playlist.ConditionTree.ToXml (BansheeQuery.FieldSet, true));
 
-            Initialize();
+            Initialize ();
 
             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.ConditionTree;
+            Limit = playlist.Limit;
+            LimitValue = playlist.LimitValue;
+            Order = playlist.QueryOrder;
+            LimitEnabled = playlist.IsLimited;
         }
     
-        public Editor () : base("SmartPlaylistEditorDialog")
+        public Editor () : base ("SmartPlaylistEditorDialog")
         {
-            Initialize();
+            Initialize ();
         }
 
-        private void Initialize()
+        private void Initialize ()
         {
             Dialog.Title = Catalog.GetString ("New Smart Playlist");
 
-            // Add the QueryBuilder widget
-            //model = new TracksQueryModel(this.playlist);
             builder = new BansheeQueryBox ();
 
-            if (playlist != null) {
-                builder.QueryNode = playlist.ConditionTree;
-            }
-
-            builder.Show();
+            builder.Show ();
             builder.Spacing = 4;
 
-            builder_box.PackStart(builder, true, true, 0);
+            builder_box.PackStart (builder, true, true, 0);
 
             name_entry.Changed += HandleNameChanged;
 
             // Model is Name, Condition, OrderBy, LimitNumber, LimitCriterion
-            ListStore list_model = new ListStore (typeof(string), typeof(string), typeof(string), 
-                typeof(string), typeof(int));
+            ListStore list_model = new ListStore (typeof(string), typeof(string), typeof(string), typeof(string), typeof(string));
 
             list_model.AppendValues (
                 Catalog.GetString ("Neglected Favorites"),
                 " (Rating > 3) AND ((strftime(\"%s\", current_timestamp) - LastPlayedStamp + 3600) > 2592000) ",
-                null, "0", 0);
+                null, "0", null);
 
             // TODO this one is broken, not supported by the condition GUI
             /*list_model.AppendValues (
@@ -90,31 +85,35 @@
 
             list_model.AppendValues (
                 Catalog.GetString ("700 MB of Favorites"),
-                " (Rating > 3) ",
-                "NumberOfPlays DESC",
+                "rating>3",
+                "PlayCount-DESC",
                 "700",
-                3);
+                "MB"
+            );
 
             list_model.AppendValues (
                 Catalog.GetString ("80 Minutes of Favorites"),
-                " (Rating > 3) ",
-                "NumberOfPlays DESC",
+                "rating>3",
+                "PlayCount-DESC",
                 "80",
-                1);
+                "minutes"
+            );
 
             list_model.AppendValues (
                 Catalog.GetString ("Unheard"),
-                " (NumberOfPlays = 0) ",
+                "playcount=0",
                 null,
                 "0",
-                0);
+                null
+            );
 
             list_model.AppendValues (
                 Catalog.GetString ("Unheard Podcasts"),
-                " (NumberOfPlays = 0) AND (lower(Uri) LIKE '%podcast%') ",
+                "playcount=0 location:podcast",
                 null,
                 "0",
-                0);
+                null
+            );
 
             adv_tree_view.Selection.Mode = SelectionMode.Multiple;
             adv_tree_view.Model = list_model;
@@ -126,19 +125,12 @@
             adv_add_button.Clicked += HandleAdvAdd;
             adv_use_button.Clicked += HandleAdvUse;
 
-            Gdk.Geometry limits = new Gdk.Geometry();
-            limits.MinWidth = Dialog.SizeRequest().Width;
-            limits.MaxWidth = Gdk.Screen.Default.Width;
-            limits.MinHeight = -1;
-            limits.MaxHeight = -1;
-            Dialog.SetGeometryHints(Dialog, limits, Gdk.WindowHints.MaxSize | Gdk.WindowHints.MinSize);
-
-            Update();
+            Update ();
             
-            name_entry.GrabFocus();
+            name_entry.GrabFocus ();
         }
 
-        /*public void SetQueryFromSearch()
+        /*public void SetQueryFromSearch ()
         {
             Banshee.Widgets.SearchEntry search_entry = InterfaceElements.SearchEntry;
 
@@ -158,7 +150,7 @@
 
             // only search for years if the query is a number
             try {
-                int.Parse(query);
+                int.Parse (query);
                 condition_candidates.Add (FilterIs.Operator.FormatValues (false, "Year", query, null) );
             }
             catch {
@@ -195,15 +187,15 @@
             name_entry.Text = search_entry.GetLabelForFilterID(search_entry.ActiveFilterID) + ": " + query;
         }*/
 
-        public void RunDialog()
+        public void RunDialog ()
         {
-            Run();
-            Dialog.Destroy();
+            Run ();
+            Dialog.Destroy ();
         }
 
-        public override ResponseType Run()
+        public override ResponseType Run ()
         {
-            Dialog.ShowAll();
+            Dialog.ShowAll ();
 
             ResponseType response = (ResponseType)Dialog.Run ();
 
@@ -220,41 +212,45 @@
 
             if (response == ResponseType.Ok) {
                 string name = PlaylistName;
-                //string condition = Condition;
-                //string order_by = OrderBy;
-                //string limit_number = LimitNumber;
-                //int limit_criterion = LimitCriterion;
+                QueryNode condition_tree = Condition;
+                QueryLimit limit = Limit;
+                QueryOrder order = Order;
+                IntegerQueryValue limit_value = LimitValue;
 
                 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);
-                        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.Condition = condition;
-                        playlist.OrderBy = order_by;
-                        playlist.LimitNumber = limit_number;
-                        playlist.LimitCriterion = limit_criterion;
-                        playlist.Commit();
+                        playlist = new SmartPlaylistSource (name);
 
-                        playlist.QueueRefresh();
+                        playlist.ConditionTree = condition_tree;
+                        playlist.QueryOrder = order;
+                        playlist.Limit = limit;
+                        playlist.LimitValue = limit_value;
+
+                        ThreadAssist.ProxyToMain (delegate {
+                            playlist.Save ();
+                            ServiceManager.SourceManager.DefaultSource.AddChildSource (playlist);
+                        });
+                        //SmartPlaylistCore.Instance.StartTimer (playlist);
+                    } else {
+                        playlist.ConditionTree = condition_tree;
+                        playlist.QueryOrder = order;
+                        playlist.LimitValue = limit_value;
+                        playlist.Limit = limit;
+
+                        ThreadAssist.ProxyToMain (delegate {
+                            playlist.Rename (name);
+                            playlist.Save ();
+                            playlist.Reload ();
+                        });
 
-                        if (playlist.TimeDependent)
-                            SmartPlaylistCore.Instance.StartTimer(playlist);
+                        /*if (playlist.TimeDependent)
+                            SmartPlaylistCore.Instance.StartTimer (playlist);
                         else
-                            SmartPlaylistCore.Instance.StopTimer();
+                            SmartPlaylistCore.Instance.StopTimer ();*/
 
-                        playlist.ListenToPlaylists();
-                        SmartPlaylistCore.Instance.SortPlaylists();*/
+                        //playlist.ListenToPlaylists ();
+                        //SmartPlaylistCore.Instance.SortPlaylists ();
                     }
                 });
             }
@@ -265,7 +261,7 @@
         private void HandleAdvSelectionChanged (object sender, EventArgs args)
         {
             TreeSelection selection = sender as TreeSelection;
-            UpdateAdvButtons (selection.CountSelectedRows());
+            UpdateAdvButtons (selection.CountSelectedRows ());
         }
 
         private void UpdateAdvButtons (int num)
@@ -278,22 +274,26 @@
         {
             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;
-                    string condition       = adv_tree_view.Model.GetValue (iter, 1) as string;
-                    string orderBy         = adv_tree_view.Model.GetValue (iter, 2) as string;
-                    string limitNumber     = adv_tree_view.Model.GetValue (iter, 3) as string;
-                    int limitCriterion  = (int) adv_tree_view.Model.GetValue (iter, 4);
-
-                    SmartPlaylistSource pl = new SmartPlaylistSource(name, condition, orderBy, limitNumber, limitCriterion);
-                    Banshee.Sources.LibrarySource.Instance.AddChildSource (pl);
-                    SmartPlaylistCore.Instance.StartTimer (pl);
+                    UserQueryParser parser = new UserQueryParser (adv_tree_view.Model.GetValue (iter, 1) as string);
+                    QueryNode condition    = parser.BuildTree (BansheeQuery.FieldSet);
+                    QueryOrder order       = BansheeQuery.FindOrder (adv_tree_view.Model.GetValue (iter, 2) as string);
+                    IntegerQueryValue val  = new IntegerQueryValue ();
+                    val.ParseUserQuery (adv_tree_view.Model.GetValue (iter, 3) as string);
+                    QueryLimit limit       = BansheeQuery.FindLimit (adv_tree_view.Model.GetValue (iter, 4) as string);
+
+                    SmartPlaylistSource pl = new SmartPlaylistSource (name, condition, order, limit, val);
+                    pl.Save ();
+                    //Banshee.Sources.LibrarySource.Instance.AddChildSource (pl);
+                    ServiceManager.SourceManager.DefaultSource.AddChildSource (pl);
+                    //SmartPlaylistCore.Instance.StartTimer (pl);
                 }
-            }*/
+            }
 
-            Dialog.Destroy();
+            Dialog.Destroy ();
         }
 
         private void HandleAdvUse (object sender, EventArgs args)
@@ -304,27 +304,34 @@
                 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);
-            }*/
+                PlaylistName     = adv_tree_view.Model.GetValue (iter, 0) as string;
+                UserQueryParser parser = new UserQueryParser (adv_tree_view.Model.GetValue (iter, 1) as string);
+                Condition        = parser.BuildTree (BansheeQuery.FieldSet);
+                Order            = BansheeQuery.FindOrder (adv_tree_view.Model.GetValue (iter, 2) as string);
+                QueryLimit limit = BansheeQuery.FindLimit (adv_tree_view.Model.GetValue (iter, 4) as string);
+                Limit            = limit;
+                LimitEnabled     = limit != null;
+
+                IntegerQueryValue val = new IntegerQueryValue ();
+                val.ParseUserQuery (adv_tree_view.Model.GetValue (iter, 3) as string);
+                LimitValue = val;
+            }
         }
 
-        private void HandleNameChanged(object sender, EventArgs args)
+        private void HandleNameChanged (object sender, EventArgs args)
         {
             Update ();
         }
 
-        private void Update()
+        private void Update ()
         {
-            if (name_entry.Text == "") {
+            if (String.IsNullOrEmpty (name_entry.Text)) {
                 ok_button.Sensitive = false;
                 //already_in_use_label.Markup = "";
             } else {
+                ok_button.Sensitive = true;
                 /*object res = Globals.Library.Db.QuerySingle(new DbCommand(
                     "SELECT Name FROM SmartPlaylists WHERE lower(Name) = lower(:name)",
                     "name", name_entry.Text
@@ -351,55 +358,29 @@
             }
         }
 
-        /*private string Condition {
-            get {
-                return builder.MatchesEnabled
-                    ? builder.MatchQuery
-                    : null;
-            }
-
-            set {
-                builder.MatchesEnabled = (value != null);
-                builder.MatchQuery = value;
-            }
+        private QueryNode Condition {
+            get { return builder.QueryNode; }
+            set { builder.QueryNode = value; }
         }
 
-        private string OrderBy {
-            get {
-                return (builder.Limit && builder.LimitNumber != "0")
-                    ? builder.OrderBy
-                    : null;
-            }
+        private QueryOrder Order {
+            get { return builder.LimitBox.Order; }
+            set { builder.LimitBox.Order = value; }
+        }
 
-            set {
-                builder.Limit = (value != null);
-                builder.OrderBy = value;
-            }
+        private IntegerQueryValue LimitValue {
+            get { return builder.LimitBox.LimitValue; }
+            set { builder.LimitBox.LimitValue = value; }
         }
 
-        private string LimitNumber {
-            get {
-                return (builder.Limit)
-                    ? builder.LimitNumber
-                    : "0";
-            }
-            
-            set {
-                if (value != null && value != "" && value != "0") {
-                    builder.Limit = true;
-                    builder.LimitNumber = value;
-                }
-            }
+        private QueryLimit Limit {
+            get { return builder.LimitBox.Limit; }
+            set { builder.LimitBox.Limit = value; }
         }
 
-        private string LimitCriterion {
-            get {
-                return (string) builder.LimitCriterion;
-            }
-            
-            set {
-                builder.LimitCriterion = Convert.ToInt32 (value);
-            }
-        }*/
+        private bool LimitEnabled {
+            get { return builder.LimitBox.Enabled; }
+            set { builder.LimitBox.Enabled = 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	Mon Feb 11 02:30:35 2008
@@ -64,6 +64,7 @@
 	Banshee.Library.Gui/ImportDialog.cs \
 	Banshee.Query.Gui/BansheeQueryBox.cs \
 	Banshee.Query.Gui/PlaylistQueryValueEntry.cs \
+	Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs \
 	Banshee.Query.Gui/RatingQueryValueEntry.cs \
 	Banshee.SmartPlaylist.Gui/Editor.cs \
 	Banshee.Sources.Gui/CellEditEntry.cs \

Modified: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/DateQueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -64,12 +64,11 @@
             combo.AppendText (Catalog.GetString ("weeks"));
             combo.AppendText (Catalog.GetString ("months"));
             combo.AppendText (Catalog.GetString ("years"));
+            combo.Realized += delegate { combo.Active = 1; };
             Add (combo);
 
             Add (new Label ("ago"));
 
-            combo.Active = 1;
-
             spin_button.ValueChanged += HandleValueChanged;
             combo.Changed += HandleValueChanged;
         }
@@ -80,7 +79,6 @@
                 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;
@@ -89,7 +87,7 @@
 
         protected void HandleValueChanged (object o, EventArgs args)
         {
-            query_value.SetRelativeValue (spin_button.ValueAsInt, factors [combo.Active]);
+            query_value.SetRelativeValue (-spin_button.ValueAsInt, factors [combo.Active]);
         }
     }
 }

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/FileSizeQueryValueEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/FileSizeQueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -0,0 +1,87 @@
+//
+// FileSizeQueryValueEntry.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 FileSizeQueryValueEntry : QueryValueEntry
+    {
+        protected SpinButton spin_button;
+        protected ComboBox combo;
+        protected FileSizeQueryValue query_value;
+
+        protected static readonly FileSizeFactor [] factors = new FileSizeFactor [] {
+            FileSizeFactor.None, FileSizeFactor.KB, FileSizeFactor.MB, FileSizeFactor.GB
+        };
+
+        // Relative: [<|>] [num] [minutes|hours] ago
+        // TODO: Absolute: [>|>=|=|<|<=] [date/time]
+        public FileSizeQueryValueEntry () : 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 ("bytes"));
+            combo.AppendText (Catalog.GetString ("KB"));
+            combo.AppendText (Catalog.GetString ("MB"));
+            combo.AppendText (Catalog.GetString ("GB"));
+            combo.Realized += delegate { combo.Active = 2; };
+            Add (combo);
+
+            spin_button.ValueChanged += HandleValueChanged;
+            combo.Changed += HandleValueChanged;
+        }
+
+        public override QueryValue QueryValue {
+            get { return query_value; }
+            set { 
+                spin_button.ValueChanged -= HandleValueChanged;
+                combo.Changed -= HandleValueChanged;
+                query_value = value as FileSizeQueryValue;
+                combo.Active = Array.IndexOf (factors, query_value.Factor);
+                spin_button.ValueChanged += HandleValueChanged;
+                combo.Changed += HandleValueChanged;
+            }
+        }
+
+        protected void HandleValueChanged (object o, EventArgs args)
+        {
+            query_value.SetValue (spin_button.ValueAsInt, factors [combo.Active]);
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/IntegerQueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -55,6 +55,7 @@
                 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);
+                query_value.SetValue (spin_button.ValueAsInt);
                 spin_button.ValueChanged += HandleValueChanged;
             }
         }

Modified: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs	(original)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryBox.cs	Mon Feb 11 02:30:35 2008
@@ -28,8 +28,11 @@
 //
 
 using System;
+using System.Collections.Generic;
 using System.Text;
 
+using Mono.Unix;
+
 using Gtk;
 using Hyena;
 using Hyena.Query;
@@ -38,83 +41,107 @@
 {
     public class QueryBox : VBox
     {
-        private QueryFieldSet field_set;
+        private QueryTermsBox terms_box;
 
-        private QueryTermBox first_row = null;
-        public QueryTermBox FirstRow {
-            get { return first_row; }
+        private QueryLimitBox limit_box;
+        public QueryLimitBox LimitBox {
+            get { return limit_box; }
         }
-        
-        public QueryBox (QueryFieldSet fieldSet) : base()
+
+        private ComboBox terms_logic_combo;
+        private CheckButton terms_enabled_checkbox;
+        private Label terms_label;
+
+        public QueryBox (QueryFieldSet fieldSet, QueryOrder [] orders, QueryLimit [] limits) : base ()
         {
-            this.field_set = fieldSet;
-            CreateRow (false);
+            //this.sorted_fields = fieldSet.Fields;
+            //this.field_set = fieldSet;
+            terms_box = new QueryTermsBox (fieldSet);
+            limit_box = new QueryLimitBox (orders, limits);
+
+            BuildInterface ();
+        }
+
+        private void BuildInterface ()
+        {
+            Alignment matchesAlignment = new Alignment (0.0f, 0.0f, 1.0f, 1.0f);
+            matchesAlignment.Show ();
+            matchesAlignment.SetPadding (5, 5, 5, 5);
+            matchesAlignment.Add (terms_box);
+        
+            Frame matchesFrame = new Frame (null);
+            matchesFrame.Show ();
+            matchesFrame.Add (matchesAlignment);
+            matchesFrame.LabelWidget = BuildMatchHeader ();
+
+            PackStart(matchesFrame, true, true, 0);
+            PackStart(limit_box, false, false, 0);
+
+            //ShowAll ();
+        }
+
+        private HBox BuildMatchHeader ()
+        {
+            HBox header = new HBox ();
+            header.Show ();
+            
+            terms_enabled_checkbox = new CheckButton (Catalog.GetString ("_Match"));
+            terms_enabled_checkbox.Show ();
+            terms_enabled_checkbox.Active = true;
+            terms_enabled_checkbox.Toggled += OnMatchCheckBoxToggled;
+            header.PackStart (terms_enabled_checkbox, false, false, 0);
+            
+            terms_logic_combo = ComboBox.NewText ();
+            terms_logic_combo.AppendText (Catalog.GetString ("all"));
+            terms_logic_combo.AppendText (Catalog.GetString ("any"));
+            terms_logic_combo.Show ();
+            terms_logic_combo.Active = 0;
+            header.PackStart (terms_logic_combo, false, false, 0);
+            
+            terms_label = new Label (Catalog.GetString ("of the following:"));
+            terms_label.Show ();
+            terms_label.Xalign = 0.0f;
+            header.PackStart (terms_label, true, true, 0);
+            
+            header.Spacing = 5;
+            
+            return header;
+        }
+
+        private void OnMatchCheckBoxToggled   (object o, EventArgs args)
+        {
+            terms_box.Sensitive = terms_enabled_checkbox.Active;
+            terms_logic_combo.Sensitive = terms_enabled_checkbox.Active;
+            terms_label.Sensitive = terms_enabled_checkbox.Active;
         }
 
         public QueryNode QueryNode {
             get {
-                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.QueryNode);
+                if (!terms_enabled_checkbox.Active) {
+                    return null;
+                }
+
+                QueryListNode node = new QueryListNode (terms_logic_combo.Active == 0 ? Keyword.And : Keyword.Or);
+                foreach (QueryNode child in terms_box.QueryNodes) {
+                    node.AddChild (child);
                 }
-                return and.Trim ();
+                return node.Trim ();
             }
             set {
-                if (value is QueryListNode) {
-                    // type = value.Keyword
-                    foreach (QueryNode child in (value as QueryListNode).Children) {
-                        AddNode (child);
+                if (value != null) {
+                    terms_enabled_checkbox.Active = true;
+                    if (value is QueryListNode) {
+                        terms_logic_combo.Active = ((value as QueryListNode).Keyword == Keyword.And) ? 0 : 1;
+                        terms_box.QueryNodes = (value as QueryListNode).Children;
+                    } else {
+                        List<QueryNode> nodes = new List<QueryNode> ();
+                        nodes.Add (value);
+                        terms_box.QueryNodes = nodes;
                     }
                 } else {
-                    // type = 'and'
-                    AddNode (value);
+                    terms_enabled_checkbox.Active = false;
                 }
             }
         }
-
-        private void AddNode (QueryNode node)
-        {
-            if (node is QueryTermNode) {
-                QueryTermBox box = CreateRow (false);
-                box.QueryNode = node as QueryTermNode;
-            } else {
-                Console.WriteLine ("Query Gui cannot handle child node: {0}", node.ToString ());
-            }
-        }
-
-        public QueryTermBox 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();
-            }
-            return row;
-        }
-        
-        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;
-        }
-        
     }
 }

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryLimitBox.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryLimitBox.cs	Mon Feb 11 02:30:35 2008
@@ -0,0 +1,135 @@
+//
+// QueryLimitBox.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 Mono.Unix;
+using Gtk;
+
+using Hyena;
+using Hyena.Query;
+
+namespace Hyena.Query.Gui
+{
+    public class QueryLimitBox : HBox
+    {
+        private CheckButton enabled_checkbox;
+        private Entry count_entry;
+        private ComboBox limit_combo;
+        private ComboBox order_combo;
+
+        private QueryOrder [] orders;
+        private QueryLimit [] limits;
+
+        public QueryLimitBox (QueryOrder [] orders, QueryLimit [] limits) : base ()
+        {
+            this.orders = orders;
+            this.limits = limits;
+
+            Spacing = 5;
+            
+            enabled_checkbox = new CheckButton (Catalog.GetString ("_Limit to"));
+            enabled_checkbox.Toggled += OnEnabledToggled;
+            
+            count_entry = new Entry ("25");
+            count_entry.SetSizeRequest (50, -1);
+            
+            limit_combo = ComboBox.NewText ();
+            foreach (QueryLimit limit in limits) {
+                limit_combo.AppendText (limit.Label);
+            }
+                
+            order_combo = ComboBox.NewText ();
+            foreach (QueryOrder order in orders) {
+                if (order != null) {
+                    order_combo.AppendText (order.Label);
+                }
+            }
+
+            PackStart (enabled_checkbox, false, false, 0);
+            PackStart (count_entry, false, false, 0);
+            PackStart (limit_combo, false, false, 0);
+            PackStart (new Label (Catalog.GetString ("selected by")), false, false, 0);
+            PackStart (order_combo, false, false, 0);
+                
+            enabled_checkbox.Active = false;
+            limit_combo.Active = 0;
+            order_combo.Active = 0;
+
+            OnEnabledToggled (null, null);
+
+            ShowAll ();
+        }
+
+        public QueryLimit Limit {
+            get { return Enabled ? limits [limit_combo.Active] : null; }
+            set {
+                if (value != null)
+                    limit_combo.Active = Array.IndexOf (limits, value);
+            }
+        }
+
+        public IntegerQueryValue LimitValue {
+            get {
+                if (!Enabled)
+                    return null;
+
+                IntegerQueryValue val = new IntegerQueryValue ();
+                val.ParseUserQuery (count_entry.Text);
+                return val.IsEmpty ? null : val;
+            }
+
+            set {
+                if (value != null && !value.IsEmpty)
+                    count_entry.Text = value.ToSql ();
+            }
+        }
+
+        public QueryOrder Order {
+            get { return Enabled ? orders [order_combo.Active] : null; }
+            set {
+                if (value != null)
+                    order_combo.Active = Array.IndexOf (orders, value);
+            }
+        }
+
+        private void OnEnabledToggled (object o, EventArgs args)
+        {
+            count_entry.Sensitive = enabled_checkbox.Active;
+            limit_combo.Sensitive = enabled_checkbox.Active;
+            order_combo.Sensitive = enabled_checkbox.Active;
+        }
+
+        public bool Enabled {
+            get { return enabled_checkbox.Active; }
+            set { enabled_checkbox.Active = value; }
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs	(original)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermBox.cs	Mon Feb 11 02:30:35 2008
@@ -36,15 +36,11 @@
 
 namespace Hyena.Query.Gui
 {
-    public class QueryTermBox : HBox
+    public class QueryTermBox
     {
-        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;
 
@@ -55,55 +51,80 @@
         private QueryField [] sorted_fields;
         //private Operator [] operators; FIXME: not used --Aaron
 
-        public QueryTermBox (QueryFieldSet fieldSet) : base ()
+        private ComboBox field_chooser;
+        public ComboBox FieldChooser {
+            get { return field_chooser; }
+        }
+
+        private ComboBox op_chooser;
+        public ComboBox OpChooser {
+            get { return op_chooser; }
+        }
+
+        private HBox value_box;
+        public HBox ValueEntry {
+            get { return value_box; }
+        }
+
+        private HBox button_box;
+        public HBox Buttons {
+            get { return button_box; }
+        }
+
+        public QueryTermBox (QueryField [] sorted_fields) : base ()
         {
-            this.sorted_fields = fieldSet.Fields;
-            //this.field_set = fieldSet;
+            this.sorted_fields = sorted_fields;
             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);
+
+            button_box = new HBox ();
+            button_box.PackStart (remove_button, false, false, 0);
+            button_box.PackStart (add_button, false, false, 0);
 
             foreach (QueryField field in sorted_fields) {
                 field_chooser.AppendText (field.Label);
             }
 
+            Show ();
             field_chooser.Active = 0;
+        }
 
-            ShowAll ();
+        public void Show ()
+        {
+            field_chooser.ShowAll ();
+            op_chooser.ShowAll ();
+            value_box.ShowAll ();
+            button_box.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))
+            if (this.field != null && (field == this.field || field.ValueType == this.field.ValueType)) {
+                this.field = field;
                 return;
+            }
 
             op_chooser.Changed -= HandleOperatorChanged;
 
@@ -116,11 +137,7 @@
             }
 
             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_box.PackStart (value_entry, false, true, 0);
             value_entry.ShowAll ();
 
             // Remove old type's operators
@@ -141,7 +158,6 @@
         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> ();
         }

Added: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryTermsBox.cs	Mon Feb 11 02:30:35 2008
@@ -0,0 +1,158 @@
+//
+// QueryTermsBox.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 System.Collections.Generic;
+
+using Gtk;
+using Hyena;
+using Hyena.Query;
+
+namespace Hyena.Query.Gui
+{
+    public class QueryTermsBox : HBox
+    {
+        private QueryFieldSet field_set;
+        private QueryField [] sorted_fields;
+        private List<QueryTermBox> terms = new List<QueryTermBox> ();
+        private VBox field_box, op_box, entry_box, button_box;
+
+        public QueryTermBox FirstRow {
+            get { return terms.Count > 0 ? terms[0] : null; }
+        }
+        
+        public QueryTermsBox (QueryFieldSet fieldSet) : base ()
+        {
+            this.field_set = fieldSet;
+
+            // Sort the fields alphabetically by their label
+            sorted_fields = new QueryField [fieldSet.Fields.Length];
+            Array.Copy (fieldSet.Fields, sorted_fields, sorted_fields.Length);
+            Array.Sort<QueryField> (sorted_fields, delegate (QueryField a, QueryField b) { return a.Label.CompareTo (b.Label); });
+
+            Spacing = 5;
+
+            field_box = new VBox ();
+            op_box = new VBox ();
+            entry_box = new VBox ();
+            button_box = new VBox ();
+
+            field_box.Spacing = 5;
+            op_box.Spacing = 5;
+            entry_box.Spacing = 5;
+            button_box.Spacing = 5;
+
+            PackStart (field_box,   false, false, 0);
+            PackStart (op_box,      false, false, 0);
+            PackStart (entry_box,   false, true, 0);
+            PackStart (button_box,  false, false, 0);
+
+            CreateRow (false);
+        }
+
+        public List<QueryNode> QueryNodes {
+            get {
+                List<QueryNode> nodes = new List<QueryNode> ();
+                foreach (QueryTermBox term_box in terms) {
+                    nodes.Add (term_box.QueryNode);
+                }
+                return nodes;
+            }
+            set {
+                foreach (QueryNode child in value) {
+                    AddNode (child);
+                }
+            }
+        }
+
+        private bool first_add_node = true;
+        private void AddNode (QueryNode node)
+        {
+            if (node is QueryTermNode) {
+                QueryTermBox box = first_add_node ? FirstRow : CreateRow (false);
+                box.QueryNode = node as QueryTermNode;
+                first_add_node = false;
+            } else {
+                Console.WriteLine ("Query Gui cannot handle child node: {0}", node.ToString ());
+            }
+        }
+
+        public QueryTermBox CreateRow (bool canDelete)
+        {
+            QueryTermBox row = new QueryTermBox (sorted_fields);
+
+            row.ValueEntry.HeightRequest = 31;
+            row.Buttons.HeightRequest = 31;
+
+            field_box.PackStart  (row.FieldChooser, false, false, 0);
+            op_box.PackStart     (row.OpChooser, false, false, 0);
+            entry_box.PackStart  (row.ValueEntry, false, false, 0);
+            button_box.PackStart (row.Buttons, false, false, 0);
+
+            row.Show ();
+
+            row.CanDelete = canDelete;
+            row.AddRequest += OnRowAddRequest;
+            row.RemoveRequest += OnRowRemoveRequest;
+
+            if (terms.Count == 0) {
+                //row.FieldBox.GrabFocus ();
+            }
+
+            terms.Add (row);
+
+            return row;
+        }
+        
+        public void OnRowAddRequest (object o, EventArgs args)
+        {
+            CreateRow (true);
+            UpdateCanDelete ();
+        }
+        
+        public void OnRowRemoveRequest (object o, EventArgs args)
+        {
+            QueryTermBox row = o as QueryTermBox;
+
+            field_box.Remove (row.FieldChooser);
+            op_box.Remove (row.OpChooser);
+            entry_box.Remove (row.ValueEntry);
+            button_box.Remove (row.Buttons);
+
+            terms.Remove (row);
+            UpdateCanDelete ();
+        }
+        
+        public void UpdateCanDelete ()
+        {
+            FirstRow.CanDelete = terms.Count > 1;
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/QueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -38,8 +38,13 @@
     {
         private static Dictionary<Type, Type> types = new Dictionary<Type, Type> ();
 
+        protected int DefaultWidth {
+            get { return 170; }
+        }
+
         public QueryValueEntry () : base ()
         {
+            Spacing = 5;
         }
 
         public abstract QueryValue QueryValue { get; set; }
@@ -84,6 +89,7 @@
             AddSubType (typeof(StringQueryValueEntry), typeof(StringQueryValue));
             AddSubType (typeof(IntegerQueryValueEntry), typeof(IntegerQueryValue));
             AddSubType (typeof(DateQueryValueEntry), typeof(DateQueryValue));
+            AddSubType (typeof(FileSizeQueryValueEntry), typeof(FileSizeQueryValue));
         }
     }
 }

Modified: trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Hyena.Gui/Hyena.Query.Gui/StringQueryValueEntry.cs	Mon Feb 11 02:30:35 2008
@@ -41,6 +41,7 @@
         public StringQueryValueEntry () : base ()
         {
             entry = new Entry ();
+            entry.WidthRequest = DefaultWidth;
             entry.Changed += HandleChanged;
             Add (entry);
         }

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	Mon Feb 11 02:30:35 2008
@@ -32,9 +32,12 @@
 	Hyena.Gui/EntryUndoAdapter.cs \
 	Hyena.Gui/GtkUtilities.cs \
 	Hyena.Query.Gui/DateQueryValueEntry.cs \
+	Hyena.Query.Gui/FileSizeQueryValueEntry.cs \
 	Hyena.Query.Gui/IntegerQueryValueEntry.cs \
 	Hyena.Query.Gui/QueryBox.cs \
 	Hyena.Query.Gui/QueryTermBox.cs \
+	Hyena.Query.Gui/QueryTermsBox.cs \
+	Hyena.Query.Gui/QueryLimitBox.cs \
 	Hyena.Query.Gui/QueryValueEntry.cs \
 	Hyena.Query.Gui/StringQueryValueEntry.cs \
 	Hyena.Widgets/RoundedFrame.cs \

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	Mon Feb 11 02:30:35 2008
@@ -62,7 +62,8 @@
             get { return "date"; }
         }
 
-        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (Equal, NotEqual, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual);
+        //protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (Equal, NotEqual, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual);
+        protected static AliasedObjectSet<Operator> operators = new AliasedObjectSet<Operator> (LessThanEqual, GreaterThanEqual);
         public override AliasedObjectSet<Operator> OperatorSet {
             get { return operators; }
         }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Query/FileSizeQueryValue.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Query/FileSizeQueryValue.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/FileSizeQueryValue.cs	Mon Feb 11 02:30:35 2008
@@ -89,6 +89,13 @@
             DetermineFactor ();
         }
 
+        public void SetValue (int value, FileSizeFactor factor)
+        {
+            this.value = (long) value * (long) factor;
+            this.factor = factor;
+            IsEmpty = false;
+        }
+
         protected void DetermineFactor ()
         {
             if (!IsEmpty && value != 0) {

Added: trunk/banshee/src/Core/Hyena/Hyena.Query/QueryLimit.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/QueryLimit.cs	Mon Feb 11 02:30:35 2008
@@ -0,0 +1,81 @@
+//
+// QueryLimit.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.Text;
+using System.Collections.Generic;
+
+namespace Hyena.Query
+{
+    public class QueryLimit
+    {
+        private string name;
+        public string Name {
+            get { return name; }
+        }
+
+        private string label;
+        public string Label {
+            get { return label; }
+            set { label = value; }
+        }
+
+        private bool row_based;
+        public bool RowBased {
+            get { return row_based; }
+        }
+
+        private int factor = 1;
+        public int Factor {
+            get { return factor; }
+        }
+
+        private string column;
+        public string Column {
+            get { return column; }
+        }
+
+        public QueryLimit (string name, string label, string column, int factor) : this (name, label, false)
+        {
+            this.column = column;
+            this.factor = factor;
+        }
+
+        public QueryLimit (string name, string label, bool row_based)
+        {
+            this.name = name;
+            this.label = label;
+            this.row_based = row_based;
+        }
+
+        public string ToSql (IntegerQueryValue limit_value)
+        {
+            return RowBased ? String.Format ("LIMIT {0}", limit_value.ToSql ()) : null;
+        }
+    }
+}

Added: trunk/banshee/src/Core/Hyena/Hyena.Query/QueryOrder.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena/Hyena.Query/QueryOrder.cs	Mon Feb 11 02:30:35 2008
@@ -0,0 +1,87 @@
+//
+// QueryOrder.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.Generic;
+
+namespace Hyena.Query
+{
+    public class QueryOrder
+    {
+        private string name;
+        public string Name {
+            get { return name; }
+        }
+
+        private string label;
+        public string Label {
+            get { return label; }
+            set { label = value; }
+        }
+
+        private string order_sql;
+        public string OrderSql {
+            get { return order_sql; }
+        }
+
+        public QueryOrder (string name, string label, string order_sql)
+        {
+            this.name = name;
+            this.label = label;
+            this.order_sql = order_sql;
+        }
+
+        public string ToSql ()
+        {
+            return String.Format ("ORDER BY {0}", order_sql);
+        }
+
+        public string PruneSql (QueryLimit limit, IntegerQueryValue limit_value)
+        {
+            /*"SELECT {0}, {1} FROM {2}", "OrderID", limit.Column, "CoreCache";
+
+            long limit = limit_value.IntValue *  limit.Factor;
+            long sum = 0;
+            int? limit_id = null;
+            foreach (result) {
+                sum += result[1];
+                if (sum >= limit) {
+                    limit_id = result[0];
+                    break;
+                }
+            }
+
+            if (limit_id != null) {
+                "DELETE FROM {0} WHERE {1} > {2}", "CoreCache", "OrderID", limit_id
+            }*/
+            return null;
+        }
+    }
+}

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	Mon Feb 11 02:30:35 2008
@@ -102,6 +102,11 @@
             return Value.ToString ();
         }
 
+        public override string ToString ()
+        {
+            return Value.ToString ();
+        }
+
         public abstract string ToSql ();
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Hyena/Makefile.am	(original)
+++ trunk/banshee/src/Core/Hyena/Makefile.am	Mon Feb 11 02:30:35 2008
@@ -38,9 +38,11 @@
 	Hyena.Query/IntegerQueryValue.cs \
 	Hyena.Query/QueryField.cs \
 	Hyena.Query/QueryFieldSet.cs \
+	Hyena.Query/QueryLimit.cs \
 	Hyena.Query/QueryListNode.cs \
 	Hyena.Query/QueryNode.cs \
 	Hyena.Query/QueryOperator.cs \
+	Hyena.Query/QueryOrder.cs \
 	Hyena.Query/QueryParser.cs \
 	Hyena.Query/QueryTermNode.cs \
 	Hyena.Query/QueryToken.cs \



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