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



Author: gburt
Date: Thu Jan 17 22:25:48 2008
New Revision: 2982
URL: http://svn.gnome.org/viewvc/banshee?rev=2982&view=rev

Log:
2008-01-17  Gabriel Burt  <gabriel burt gmail com>

	* src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs:
	Add English aliases to all QueryFields in addition to the translated ones,
	so English aliases will always be available.  Change the mimetype field to
	match on both MimeType and Uri columns.

	* src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs: Add
	SmartPlaylistCore service.

	* src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs: Move old
	smart playlist operator code here for migration purposes.  Migration now
	works for all conditions except time-based ones (LastPlayed/DateAdded),
	but the migrated result is only saved during each session until the
	migrator is finalized.

	* src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs: Explicitly
	pass the operator to use for 'match similar' actions.

	* src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/QueryBuilder.cs:
	Move much old code that will be replaced with Hyena.Data.Query code.  Move
	code needed to migrate from old format to new format into Migrator.

	* src/Core/Hyena/Hyena.Data.Query/QueryField.cs: Add a translated label to
	Field, split FieldSet off into it's own file.

	* src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs:
	* src/Core/Hyena/Hyena.Data.Query/QueryNode.cs: Change ToXml method to
	take a QueryFieldSet.  Change ToString to output the user query.

	* src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs: Fix whitespace, change
	Sql appends to use Field's FormatSql method allowing for a field to map to
	multiple database columns.

	* src/Core/Hyena/Makefile.am: Add QueryFieldSet.


Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/QueryBuilder.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs
   trunk/banshee/src/Core/Hyena/Makefile.am

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	Thu Jan 17 22:25:48 2008
@@ -85,9 +85,9 @@
                 Console.WriteLine ("query: {0}", Filter);
                 //Console.WriteLine ("tree:");
                 //n.Dump ();
-                Console.WriteLine ("Xml for Query: {0}", n.ToXml ());
+                Console.WriteLine ("Xml for Query: {0}", n.ToXml (field_set));
                 Console.WriteLine ("Sql for Query: {0}", filter_query);
-                Hyena.Data.Query.QueryParser qp2 = new XmlQueryParser (n.ToXml ());
+                Hyena.Data.Query.QueryParser qp2 = new XmlQueryParser (n.ToXml (field_set));
                 QueryNode n2 = qp2.BuildTree ();
                 if (n2 != null) {
                     Console.WriteLine ("User query for Xml: {0}", n2.ToUserQuery ());
@@ -98,7 +98,7 @@
                     filter_query = null;
             }
         }
-        
+
         private string AscDesc ()
         {
             return sort_column.SortType == SortType.Ascending ? " ASC" : " DESC";
@@ -407,6 +407,10 @@
             get { return provider.Where; }
         }
 
+        public static QueryFieldSet FieldSet {
+            get { return field_set; }
+        }
+        
         public override QueryField ArtistField {
             get { return field_set.Fields [0]; }
         }
@@ -417,65 +421,92 @@
 
         protected static QueryFieldSet field_set = new QueryFieldSet (
             new QueryField (
-                Catalog.GetString ("Artist"), "CoreArtists.Name", QueryFieldType.Text, true,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("by"), Catalog.GetString ("artist"), Catalog.GetString ("artists")
+                "artist", Catalog.GetString ("Artist"), "CoreArtists.Name", QueryFieldType.Text, true,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("by"), Catalog.GetString ("artist"), Catalog.GetString ("artists"),
+                "by", "artist", "artists"
             ),
             new QueryField (
-                Catalog.GetString ("Album"), "CoreAlbums.Title", QueryFieldType.Text, true,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("on"), Catalog.GetString ("album"), Catalog.GetString ("from")
+                "album", Catalog.GetString ("Album"), "CoreAlbums.Title", QueryFieldType.Text, true,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("on"), Catalog.GetString ("album"), Catalog.GetString ("from"),
+                "on", "album", "from", "albumtitle"
             ),
             new QueryField (
-                Catalog.GetString ("Track Title"), "CoreTracks.Title", QueryFieldType.Text, true,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("title"), Catalog.GetString ("titled")
+                "title", Catalog.GetString ("Track Title"), "CoreTracks.Title", QueryFieldType.Text, 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 (
-                Catalog.GetString ("Year"), "CoreTracks.Year", QueryFieldType.Numeric,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("year"), Catalog.GetString ("released")
+                "year", Catalog.GetString ("Year"), "CoreTracks.Year", QueryFieldType.Numeric,
+                // 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 (
-                Catalog.GetString ("Rating"), "CoreTracks.Rating", QueryFieldType.Numeric,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("rating"), Catalog.GetString ("stars")
+                "rating", Catalog.GetString ("Rating"), "CoreTracks.Rating", QueryFieldType.Numeric,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("rating"), Catalog.GetString ("stars"),
+                "rating", "stars"
             ),
             new QueryField (
-                Catalog.GetString ("Play Count"), "CoreTracks.PlayCount", QueryFieldType.Numeric,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("plays"), Catalog.GetString ("playcount")
+                "playcount", Catalog.GetString ("Play Count"), "CoreTracks.PlayCount", QueryFieldType.Numeric,
+                // 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 (
-                Catalog.GetString ("Skip Count"), "CoreTracks.SkipCount", QueryFieldType.Numeric,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("skips"), Catalog.GetString ("skipcount")
+                "skipcount", Catalog.GetString ("Skip Count"), "CoreTracks.SkipCount", QueryFieldType.Numeric,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("skips"), Catalog.GetString ("skipcount"),
+                "skips", "skipcount"
             ),
             new QueryField (
-                Catalog.GetString ("File Size"), "CoreTracks.FileSize", QueryFieldType.Numeric, FileSizeModifier,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("size"), Catalog.GetString ("filesize")
+                "filesize", Catalog.GetString ("File Size"), "CoreTracks.FileSize", QueryFieldType.Numeric, FileSizeModifier,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("size"), Catalog.GetString ("filesize"),
+                "size", "filesize"
             ),
             new QueryField (
-                Catalog.GetString ("File Path"), "CoreTracks.Uri", QueryFieldType.Text,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("path"), Catalog.GetString ("file"), Catalog.GetString ("uri")
+                "uri", Catalog.GetString ("File Path"), "CoreTracks.Uri", QueryFieldType.Text,
+                // 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 (
-                Catalog.GetString ("Mime Type"), "CoreTracks.MimeType", QueryFieldType.Text,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("type"), Catalog.GetString ("mimetype"), Catalog.GetString ("format")
+                "duration", Catalog.GetString ("Duration"), "CoreTracks.Duration", QueryFieldType.Numeric,
+                // 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 (
-                Catalog.GetString ("Last Played Date"), "CoreTracks.LastPlayedStamp", QueryFieldType.Numeric, DateTimeModifier,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("lastplayed"), Catalog.GetString ("played")
+                "mimetype", Catalog.GetString ("Mime Type"), "CoreTracks.MimeType {0} OR CoreTracks.Uri {0}", QueryFieldType.Text,
+                // 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 (
-                Catalog.GetString ("Imported Date"), "CoreTracks.DateAddedStamp", QueryFieldType.Numeric, DateTimeModifier,
-                // Translators: These are search fields.  Please, no spaces. Duplicates ok.
-                Catalog.GetString ("addedon"), Catalog.GetString ("dateadded"), Catalog.GetString ("importedon")
-            )
+                    // (strftime("%s", current_timestamp) - DateAddedStamp + 3600)
+                "lastplayed", Catalog.GetString ("Last Played Date"), "CoreTracks.LastPlayedStamp", QueryFieldType.Numeric, DateTimeModifier,
+                // 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", QueryFieldType.Numeric, DateTimeModifier,
+                // 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 (
+                "playlist", Catalog.GetString ("Playlist"),
+                "CoreTracks.TrackID IN (SELECT TrackID FROM CorePlaylistEntries WHERE PlaylistID {0})",
+                QueryFieldType.Numeric,
+                // Translators: These are unique search fields.  Please, no spaces. Blank ok.
+                Catalog.GetString ("duration"), Catalog.GetString ("length"), Catalog.GetString ("time"),
+                "duration", "length", "time"
+            )*/
         );
         
         private static string FileSizeModifier (string input)

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs	Thu Jan 17 22:25:48 2008
@@ -60,7 +60,7 @@
             RegisterService<DBusServiceManager> ();
             RegisterService<BansheeDbConnection> ();
             RegisterService<SourceManager> ();
-            //RegisterService<SmartPlaylistCore> ();
+            RegisterService<SmartPlaylistCore> ();
             RegisterService<ProfileManager> ();
             RegisterService<PlayerEngineService> ();
             RegisterService<PlaybackControllerService> ();

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs	Thu Jan 17 22:25:48 2008
@@ -5,7 +5,7 @@
 //   Gabriel Burt <gburt novell com>
 //
 // Copyright (C) 2006-2007 Gabriel Burt
-// Copyright (C) 2007 Novell, Inc.
+// 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
@@ -28,18 +28,25 @@
 //
 
 using System;
+using System.Text.RegularExpressions;
+using System.Collections;
 using System.Collections.Generic;
 
+using Hyena.Data;
+using Hyena.Data.Query;
+
+using Banshee.Collection.Database;
+
 namespace Banshee.SmartPlaylist
 {
-    public class Migrator
+    internal class Migrator
     {
         private string [] criteria = new string [] { "tracks", "minutes", "hours", "MB" };
         private Dictionary<string, Order> order_hash = new Dictionary<string, Order> ();
 
         public static void MigrateAll ()
         {
-            Console.WriteLine ("Migrating All..........");
+            Console.WriteLine ("Migrating All.");
             Migrator m = new Migrator ();
             foreach (SmartPlaylistSource source in SmartPlaylistSource.LoadAll ())
                 m.Migrate (source);
@@ -71,20 +78,20 @@
                 source.OrderDir = order.Dir;
             }
             source.LimitCriterion = criteria [Convert.ToInt32 (source.LimitCriterion)];
+            Console.WriteLine ("Befor: {0}", source.Condition);
             source.Condition = ParseCondition (source.Condition);
-            source.Save ();
+            Console.WriteLine ("After: {0}\n", source.Condition);
+            //source.Save ();
         }
 
         private string ParseCondition (string value)
         {
-            /*
             // Check for ANDs or ORs and split into conditions as needed
             string [] conditions;
-            bool ands;
+            bool ands = true;
             if (value.IndexOf(") AND (") != -1) {
                 ands = true;
                 conditions = System.Text.RegularExpressions.Regex.Split (value, "\\) AND \\(");
-                
             } else if (value.IndexOf(") OR (") != -1) {
                 ands = false;
                 conditions = System.Text.RegularExpressions.Regex.Split (value, "\\) OR \\(");
@@ -92,6 +99,8 @@
                 conditions = new string [] {value};
             }
 
+            QueryListNode root = new QueryListNode (ands ? Keyword.And : Keyword.Or);
+
             // Remove leading spaces and parens from the first condition
             conditions[0] = conditions[0].Remove(0, 2);
 
@@ -105,13 +114,87 @@
             foreach (string condition in conditions) {
                 // Add a new row for this condition
                 string col, v1, v2;
-                bool found_filter = false;
-                foreach (QueryFilter filter in QueryFilter.Filters) {
-                    if (filter.Operator.MatchesCondition (condition, out col, out v1, out v2)) {
-                        //Console.WriteLine ("{0} is col: {1} with v1: {2} v2: {3}", condition, col, v1, v2);
+                foreach (QueryOperator op in QueryOperator.Operators) {
+                    if (op.MatchesCondition (condition, out col, out v1, out v2)) {
+                        QueryTermNode term = new QueryTermNode ();
+                        QueryField field = TrackListDatabaseModel.FieldSet.GetByAlias (col);
+                        if (field == null) {
+                            if (col.IndexOf ("DateAddedStamp") != -1) {
+                                field = TrackListDatabaseModel.FieldSet.GetByAlias ("added");
+                            } else if (col.IndexOf ("LastPlayedStamp") != -1) {
+                                field = TrackListDatabaseModel.FieldSet.GetByAlias ("lastplayed");
+                            }
+
+                            if (field == null) {
+                                Console.WriteLine ("got unknown field {0} so skipping", col);
+                                continue;
+                            }
+                        }
+                        term.Field = field.PrimaryAlias;
+                        term.Value = v1;
+                        root.AddChild (term);
+
+                        switch (op.GetType ().ToString ()) {
+                            case "EQText":
+                            case "EQ":
+                                term.Operator = Operator.GetByUserOperator ("==");
+                                break;
+
+                            case "NotEQText":
+                            case "NotEQ":
+                                term.Operator = Operator.GetByUserOperator ("!=");
+                                break;
+
+                            case "Between":
+                            case "LT":
+                                term.Operator = Operator.GetByUserOperator ("<");
+                                break;
+
+                            case "GT":
+                                term.Operator = Operator.GetByUserOperator (">");
+                                break;
+
+                            case "GTE":
+                                term.Operator = Operator.GetByUserOperator (">=");
+                                break;
+
+                            case "Like":
+                                term.Operator = Operator.GetByUserOperator (":");
+                                break;
+
+                            case "NotLike":
+                                term.Operator = Operator.GetByUserOperator (":");
+                                break;
+
+                            case "StartsWith":
+                                term.Operator = Operator.GetByUserOperator ("=");
+                                break;
+
+                            case "InPlaylist":
+                                term.Operator = Operator.GetByUserOperator (">");
+                                break;
+
+                            case "NotInPlaylist":
+                                term.Operator = Operator.GetByUserOperator (">");
+                                break;
+
+                            case "InSmartPlaylist":
+                                term.Operator = Operator.GetByUserOperator (">");
+                                break;
+
+                            case "NotInSmartPlaylist":
+                                term.Operator = Operator.GetByUserOperator (">");
+                                break;
+                        }
+
+                        if (term.Field.IndexOf ("LastPlayedStamp") != -1) {
+                        } else if (term.Field.IndexOf ("DateAddedStamp") != -1) {
+                        }
+
+                        Console.WriteLine ("..{0}\n=>{1}", condition, term.ToString ());
 
                         // Set the column
-                        QueryBuilderMatchRow row = matchesBox.Children[count] as QueryBuilderMatchRow;
+                        /*QueryBuilderMatchRow row = matchesBox.Children[count] as QueryBuilderMatchRow;
                         if (!ComboBoxUtil.SetActiveString (row.FieldBox, model.GetName(col))) {
                             if (col.IndexOf ("current_timestamp") == -1) {
                                 Console.WriteLine ("Found col that can't place");
@@ -131,41 +214,20 @@
                                     break;
                                 }
                             }
-                        }
-
-                        // Make sure we're on the right filter (as multiple filters can have the same operator)
-                        QueryFilter real_filter = filter;
-                        if (System.Array.IndexOf (row.Match.ValidFilters, filter) == -1) {
-                            foreach (QueryFilter f in row.Match.ValidFilters) {
-                                if (f.Operator == filter.Operator) {
-                                    real_filter = f;
-                                    break;
-                                }
-                            }
-                        }
-
-                        // Set the operator
-                        if (!ComboBoxUtil.SetActiveString (row.FilterBox, real_filter.Name)) {
-                            Console.WriteLine ("Found filter that can't place");
-                            break;
-                        }
+                        }*/
 
                         // Set the values
-                        row.Match.Value1 = v1;
-                        row.Match.Value2 = v2;
+                        //v1;
+                        //v2;
 
-                        found_filter = true;
                         break;
                     }
                 }
 
-                // TODO should push error here instead
-                if (!found_filter)
-                    Console.WriteLine ("Couldn't find appropriate filter for condition: {0}", condition);
                 count++;
             }
-        */
-            return "CoreTracks.Rating > 0";
+
+            return root.Trim ().ToXml (TrackListDatabaseModel.FieldSet);
         }
 
         private Order FindOrder (string key)
@@ -181,5 +243,232 @@
             }
             return default(Order);
         }
+
+        public sealed class QueryOperator
+        {
+            private string format;
+
+            public string Format {
+                get { return format; }
+            }
+
+            private QueryOperator (string format)
+            {
+                this.format = format;
+            }
+
+            public string FormatValues (bool text, string column, string value1, string value2)
+            {
+                if (text)
+                    return String.Format (format, "'", column, value1, value2);
+                else
+                    return String.Format (format, "", column, value1, value2);
+            }
+
+            public bool MatchesCondition (string condition, out string column, out string value1, out string value2) {
+                // Remove trailing parens from the end of the format b/c trailing parens are trimmed from the condition
+                string regex = String.Format(format.Replace("(", "\\(").Replace(")", "\\)"),
+                        "'?",   // ignore the single quotes if they exist
+                        "(.*)", // match the column
+                        "(.*)", // match the first value
+                        "(.*)"  // match the second value
+                );
+
+
+                //Console.WriteLine ("regex = {0}", regex);
+                MatchCollection mc = System.Text.RegularExpressions.Regex.Matches (condition, regex);
+                if (mc != null && mc.Count > 0 && mc[0].Groups.Count > 0) {
+                    column = mc[0].Groups[1].Captures[0].Value;
+                    value1 = mc[0].Groups[2].Captures[0].Value.Trim(new char[] {'\''});
+
+                    if (mc[0].Groups.Count == 4)
+                        value2 = mc[0].Groups[3].Captures[0].Value.Trim(new char[] {'\''});
+                    else
+                        value2 = null;
+
+                    return true;
+                } else {
+                    column = value1 = value2 = null;
+                    return false;
+                }
+            }
+
+            // calling lower() to have case insensitive comparisons with strings
+            public static QueryOperator EQText     = new QueryOperator("lower({1}) = {0}{2}{0}");
+            public static QueryOperator NotEQText  = new QueryOperator("lower({1}) != {0}{2}{0}");
+
+            public static QueryOperator EQ         = new QueryOperator("{1} = {0}{2}{0}");
+            public static QueryOperator NotEQ      = new QueryOperator("{1} != {0}{2}{0}");
+            public static QueryOperator Between    = new QueryOperator("{1} BETWEEN {0}{2}{0} AND {0}{3}{0}");
+            public static QueryOperator LT         = new QueryOperator("{1} < {0}{2}{0}");
+            public static QueryOperator GT         = new QueryOperator("{1} > {0}{2}{0}");
+            public static QueryOperator GTE        = new QueryOperator("{1} >= {0}{2}{0}");
+
+            // Note, the following lower() calls are necessary b/c of a sqlite bug which makes the LIKE
+            // command case sensitive with certain characters.
+            public static QueryOperator Like       = new QueryOperator("lower({1}) LIKE '%{2}%'");
+            public static QueryOperator NotLike    = new QueryOperator("lower({1}) NOT LIKE '%{2}%'");
+            public static QueryOperator StartsWith = new QueryOperator("lower({1}) LIKE '{2}%'");
+            public static QueryOperator EndsWith   = new QueryOperator("lower({1}) LIKE '%{2}'");
+
+            // TODO these should either be made generic or moved somewhere else since they are Banshee/Track/Playlist specific.
+            public static QueryOperator InPlaylist      = new QueryOperator("TrackID IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
+            public static QueryOperator NotInPlaylist   = new QueryOperator("TrackID NOT IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
+
+            public static QueryOperator InSmartPlaylist      = new QueryOperator("TrackID IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
+            public static QueryOperator NotInSmartPlaylist   = new QueryOperator("TrackID NOT IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
+
+            public static QueryOperator [] Operators = new QueryOperator [] {
+                EQText, NotEQText, EQ, NotEQ, Between, LT, GT, GTE, Like, NotLike,
+                StartsWith, InPlaylist, NotInPlaylist, InSmartPlaylist, NotInSmartPlaylist
+            };
+        }
+
+        /*public sealed class QueryFilter
+        {
+            private string name;
+            private QueryOperator op;
+
+            public string Name {
+                get { return name; }
+            }
+
+            public QueryOperator Operator {
+                get { return op; }
+            }
+
+            private static Hashtable filters = new Hashtable();
+            private static ArrayList filters_array = new ArrayList();
+            public static QueryFilter GetByName (string name)
+            {
+                return filters[name] as QueryFilter;
+            }
+
+            public static ArrayList Filters {
+                get { return filters_array; }
+            }
+
+            private static QueryFilter NewOperation (string name, QueryOperator op)
+            {
+                QueryFilter filter = new QueryFilter(name, op);
+                filters[name] = filter;
+                filters_array.Add (filter);
+                return filter;
+            }
+
+            private QueryFilter (string name, QueryOperator op)
+            {
+                this.name = name;
+                this.op = op;
+            }
+
+            public static QueryFilter InPlaylist = NewOperation (
+                Catalog.GetString ("is"),
+                QueryOperator.InPlaylist
+            );
+
+            public static QueryFilter NotInPlaylist = NewOperation (
+                Catalog.GetString ("is not"),
+                QueryOperator.NotInPlaylist
+            );
+
+            public static QueryFilter InSmartPlaylist = NewOperation (
+                Catalog.GetString ("is"),
+                QueryOperator.InSmartPlaylist
+            );
+
+            public static QueryFilter NotInSmartPlaylist = NewOperation (
+                Catalog.GetString ("is not"),
+                QueryOperator.NotInSmartPlaylist
+            );
+        
+            // caution: the equal/not-equal operators for text fields (TextIs and TextNotIs) have to be defined
+            // before the ones for non-text fields. Otherwise MatchesCondition will not return the right column names.
+            // (because the regular expression for non-string fields machtes also for string fields)
+            public static QueryFilter TextIs = NewOperation (
+                Catalog.GetString ("is"),
+                QueryOperator.EQText
+            );
+
+            public static QueryFilter TextIsNot = NewOperation (
+                Catalog.GetString ("is not"),
+                QueryOperator.NotEQText
+            );
+
+            public static QueryFilter Is = NewOperation (
+                Catalog.GetString ("is"),
+                QueryOperator.EQ
+            );
+
+            public static QueryFilter IsNot = NewOperation (
+                Catalog.GetString ("is not"),
+                QueryOperator.NotEQ
+            );
+
+            public static QueryFilter IsLessThan = NewOperation (
+                Catalog.GetString ("is less than"),
+                QueryOperator.LT
+            );
+
+            public static QueryFilter IsGreaterThan = NewOperation (
+                Catalog.GetString ("is greater than"),
+                QueryOperator.GT
+            );
+
+            public static QueryFilter MoreThan = NewOperation (
+                Catalog.GetString ("more than"),
+                QueryOperator.GT
+            );
+
+            public static QueryFilter LessThan = NewOperation (
+                Catalog.GetString ("less than"),
+                QueryOperator.LT
+            );
+
+            public static QueryFilter IsAtLeast = NewOperation (
+                Catalog.GetString ("is at least"),
+                QueryOperator.GTE
+            );
+
+            public static QueryFilter Contains = NewOperation (
+                Catalog.GetString ("contains"),
+                QueryOperator.Like
+            );
+
+            public static QueryFilter DoesNotContain = NewOperation (
+                Catalog.GetString ("does not contain"),
+                QueryOperator.NotLike
+            );
+
+            public static QueryFilter StartsWith = NewOperation (
+                Catalog.GetString ("starts with"),
+                QueryOperator.StartsWith
+            );
+
+            public static QueryFilter EndsWith = NewOperation (
+                Catalog.GetString ("ends with"),
+                QueryOperator.EndsWith
+            );
+
+            public static QueryFilter IsBefore = NewOperation (
+                Catalog.GetString ("is before"),
+                QueryOperator.LT
+            );
+
+            public static QueryFilter IsAfter = NewOperation (
+                Catalog.GetString ("is after"),
+                QueryOperator.GT
+            );
+
+            public static QueryFilter IsInTheRange = NewOperation (
+                Catalog.GetString ("is between"),
+                QueryOperator.Between
+            );
+
+            public static QueryFilter Between = NewOperation (
+                Catalog.GetString ("between"),
+                QueryOperator.Between
+            );
+        }*/
     }
 }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/TrackActions.cs	Thu Jan 17 22:25:48 2008
@@ -355,7 +355,7 @@
             Source source = ServiceManager.SourceManager.ActiveSource;
             ITrackModelSource track_source = source as ITrackModelSource;
             foreach (TrackInfo track in TrackSelector.GetSelectedTracks ()) {
-                source.FilterQuery = track_source.TrackModel.ArtistField.ToTermString (track.ArtistName);
+                source.FilterQuery = track_source.TrackModel.ArtistField.ToTermString (":", track.ArtistName);
                 break;
             }
         }
@@ -365,7 +365,7 @@
             Source source = ServiceManager.SourceManager.ActiveSource;
             ITrackModelSource track_source = source as ITrackModelSource;
             foreach (TrackInfo track in TrackSelector.GetSelectedTracks ()) {
-                source.FilterQuery = track_source.TrackModel.AlbumField.ToTermString (track.AlbumTitle);
+                source.FilterQuery = track_source.TrackModel.AlbumField.ToTermString (":", track.AlbumTitle);
                 break;
             }
         }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/QueryBuilder.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/QueryBuilder.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/QueryBuilder.cs	Thu Jan 17 22:25:48 2008
@@ -37,228 +37,6 @@
 
 namespace Banshee.SmartPlaylist
 {
-    public sealed class QueryOperator
-    {
-        private string format;
-
-        public string Format {
-            get { return format; }
-        }
-
-        private QueryOperator (string format)
-        {
-            this.format = format;
-        }
-
-        public string FormatValues (bool text, string column, string value1, string value2)
-        {
-            if (text)
-                return String.Format (format, "'", column, value1, value2);
-            else
-                return String.Format (format, "", column, value1, value2);
-        }
-
-        public bool MatchesCondition (string condition, out string column, out string value1, out string value2) {
-            // Remove trailing parens from the end of the format b/c trailing parens are trimmed from the condition
-            string regex = String.Format(format.Replace("(", "\\(").Replace(")", "\\)"),
-                    "'?",   // ignore the single quotes if they exist
-                    "(.*)", // match the column
-                    "(.*)", // match the first value
-                    "(.*)"  // match the second value
-            );
-
-
-            //Console.WriteLine ("regex = {0}", regex);
-            MatchCollection mc = System.Text.RegularExpressions.Regex.Matches (condition, regex);
-            if (mc != null && mc.Count > 0 && mc[0].Groups.Count > 0) {
-                column = mc[0].Groups[1].Captures[0].Value;
-                value1 = mc[0].Groups[2].Captures[0].Value.Trim(new char[] {'\''});
-
-                if (mc[0].Groups.Count == 4)
-                    value2 = mc[0].Groups[3].Captures[0].Value.Trim(new char[] {'\''});
-                else
-                    value2 = null;
-
-                return true;
-            } else {
-                column = value1 = value2 = null;
-                return false;
-            }
-        }
-
-        // calling lower() to have case insensitive comparisons with strings
-        public static QueryOperator EQText     = new QueryOperator("lower({1}) = {0}{2}{0}");
-        public static QueryOperator NotEQText  = new QueryOperator("lower({1}) != {0}{2}{0}");
-
-        public static QueryOperator EQ         = new QueryOperator("{1} = {0}{2}{0}");
-        public static QueryOperator NotEQ      = new QueryOperator("{1} != {0}{2}{0}");
-        public static QueryOperator Between    = new QueryOperator("{1} BETWEEN {0}{2}{0} AND {0}{3}{0}");
-        public static QueryOperator LT         = new QueryOperator("{1} < {0}{2}{0}");
-        public static QueryOperator GT         = new QueryOperator("{1} > {0}{2}{0}");
-        public static QueryOperator GTE        = new QueryOperator("{1} >= {0}{2}{0}");
-
-        // Note, the following lower() calls are necessary b/c of a sqlite bug which makes the LIKE
-        // command case sensitive with certain characters.
-        public static QueryOperator Like       = new QueryOperator("lower({1}) LIKE '%{2}%'");
-        public static QueryOperator NotLike    = new QueryOperator("lower({1}) NOT LIKE '%{2}%'");
-        public static QueryOperator StartsWith = new QueryOperator("lower({1}) LIKE '{2}%'");
-        public static QueryOperator EndsWith   = new QueryOperator("lower({1}) LIKE '%{2}'");
-
-        // TODO these should either be made generic or moved somewhere else since they are Banshee/Track/Playlist specific.
-        public static QueryOperator InPlaylist      = new QueryOperator("TrackID IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
-        public static QueryOperator NotInPlaylist   = new QueryOperator("TrackID NOT IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
-
-        public static QueryOperator InSmartPlaylist      = new QueryOperator("TrackID IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
-        public static QueryOperator NotInSmartPlaylist   = new QueryOperator("TrackID NOT IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
-    }
-
-    public sealed class QueryFilter
-    {
-        private string name;
-        private QueryOperator op;
-
-        public string Name {
-            get { return name; }
-        }
-
-        public QueryOperator Operator {
-            get { return op; }
-        }
-
-        private static Hashtable filters = new Hashtable();
-        private static ArrayList filters_array = new ArrayList();
-        public static QueryFilter GetByName (string name)
-        {
-            return filters[name] as QueryFilter;
-        }
-
-        public static ArrayList Filters {
-            get { return filters_array; }
-        }
-
-        private static QueryFilter NewOperation (string name, QueryOperator op)
-        {
-            QueryFilter filter = new QueryFilter(name, op);
-            filters[name] = filter;
-            filters_array.Add (filter);
-            return filter;
-        }
-
-        private QueryFilter (string name, QueryOperator op)
-        {
-            this.name = name;
-            this.op = op;
-        }
-
-        public static QueryFilter InPlaylist = NewOperation (
-            Catalog.GetString ("is"),
-            QueryOperator.InPlaylist
-        );
-
-        public static QueryFilter NotInPlaylist = NewOperation (
-            Catalog.GetString ("is not"),
-            QueryOperator.NotInPlaylist
-        );
-
-        public static QueryFilter InSmartPlaylist = NewOperation (
-            Catalog.GetString ("is"),
-            QueryOperator.InSmartPlaylist
-        );
-
-        public static QueryFilter NotInSmartPlaylist = NewOperation (
-            Catalog.GetString ("is not"),
-            QueryOperator.NotInSmartPlaylist
-        );
-    
-        // caution: the equal/not-equal operators for text fields (TextIs and TextNotIs) have to be defined
-        // before the ones for non-text fields. Otherwise MatchesCondition will not return the right column names.
-        // (because the regular expression for non-string fields machtes also for string fields)
-        public static QueryFilter TextIs = NewOperation (
-            Catalog.GetString ("is"),
-            QueryOperator.EQText
-        );
-
-        public static QueryFilter TextIsNot = NewOperation (
-            Catalog.GetString ("is not"),
-            QueryOperator.NotEQText
-        );
-
-        public static QueryFilter Is = NewOperation (
-            Catalog.GetString ("is"),
-            QueryOperator.EQ
-        );
-
-        public static QueryFilter IsNot = NewOperation (
-            Catalog.GetString ("is not"),
-            QueryOperator.NotEQ
-        );
-
-        public static QueryFilter IsLessThan = NewOperation (
-            Catalog.GetString ("is less than"),
-            QueryOperator.LT
-        );
-
-        public static QueryFilter IsGreaterThan = NewOperation (
-            Catalog.GetString ("is greater than"),
-            QueryOperator.GT
-        );
-
-        public static QueryFilter MoreThan = NewOperation (
-            Catalog.GetString ("more than"),
-            QueryOperator.GT
-        );
-
-        public static QueryFilter LessThan = NewOperation (
-            Catalog.GetString ("less than"),
-            QueryOperator.LT
-        );
-
-        public static QueryFilter IsAtLeast = NewOperation (
-            Catalog.GetString ("is at least"),
-            QueryOperator.GTE
-        );
-
-        public static QueryFilter Contains = NewOperation (
-            Catalog.GetString ("contains"),
-            QueryOperator.Like
-        );
-
-        public static QueryFilter DoesNotContain = NewOperation (
-            Catalog.GetString ("does not contain"),
-            QueryOperator.NotLike
-        );
-
-        public static QueryFilter StartsWith = NewOperation (
-            Catalog.GetString ("starts with"),
-            QueryOperator.StartsWith
-        );
-
-        public static QueryFilter EndsWith = NewOperation (
-            Catalog.GetString ("ends with"),
-            QueryOperator.EndsWith
-        );
-
-        public static QueryFilter IsBefore = NewOperation (
-            Catalog.GetString ("is before"),
-            QueryOperator.LT
-        );
-
-        public static QueryFilter IsAfter = NewOperation (
-            Catalog.GetString ("is after"),
-            QueryOperator.GT
-        );
-
-        public static QueryFilter IsInTheRange = NewOperation (
-            Catalog.GetString ("is between"),
-            QueryOperator.Between
-        );
-
-        public static QueryFilter Between = NewOperation (
-            Catalog.GetString ("between"),
-            QueryOperator.Between
-        );
-    }
-    
     public static class ComboBoxUtil
     {
         public static string GetActiveString(ComboBox box)
@@ -266,7 +44,6 @@
             TreeIter iter;
             if(!box.GetActiveIter(out iter))
                 return null;
-
                 
             return (string)box.Model.GetValue(iter, 0);
         }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs	Thu Jan 17 22:25:48 2008
@@ -38,37 +38,39 @@
         Text,
         Numeric
     }
-    
+
     public class QueryField
     {
         public delegate string ModifierHandler (string input);
 
         public string Name;
+        public string Label;
         public string [] Aliases;
         public string Column;
         public bool Default;
         public QueryFieldType QueryFieldType;
         public ModifierHandler Modifier;
 
-        public QueryField (string name, string column, QueryFieldType type, params string [] aliases) 
-            : this (name, column, type, false, null, aliases)
+        public QueryField (string name, string label, string column, QueryFieldType type, params string [] aliases) 
+            : this (name, label, column, type, false, null, aliases)
         {
         }
         
-        public QueryField (string name, string column, QueryFieldType type, ModifierHandler modifier, params string [] aliases) 
-            : this (name, column, type, false, modifier, aliases)
+        public QueryField (string name, string label, string column, QueryFieldType type, ModifierHandler modifier, params string [] aliases) 
+            : this (name, label, column, type, false, modifier, aliases)
         {
         }
         
-        public QueryField (string name, string column, QueryFieldType type, bool isDefault, params string [] aliases)
-            : this (name, column, type, isDefault, null, aliases)
+        public QueryField (string name, string label, string column, QueryFieldType type, bool isDefault, params string [] aliases)
+            : this (name, label, column, type, isDefault, null, aliases)
         {
         }
         
-        public QueryField (string name, string column, QueryFieldType type, bool isDefault, 
+        public QueryField (string name, string label, string column, QueryFieldType type, bool isDefault, 
             ModifierHandler modifier, params string [] aliases)
         {
             Name = name;
+            Label = label;
             Column = column;
             QueryFieldType = type;
             Default = isDefault;
@@ -80,33 +82,29 @@
             get { return Aliases [0]; }
         }
 
-        public string ToTermString (string value)
+        public string ToTermString (string op, string value)
         {
-            return String.Format ("{0}:{2}{1}{2}",
-                PrimaryAlias, value, value.IndexOf (" ") == -1 ? "" : "\""
-            );
+            return ToTermString (PrimaryAlias, op, value);
         }
-    }
-
-    public class QueryFieldSet
-    {
-        private Dictionary<string, QueryField> map = new Dictionary<string, QueryField> ();
-        private QueryField [] fields;
 
-        public QueryFieldSet (params QueryField [] fields)
+        public string FormatSql (string format, params object [] args)
         {
-            this.fields = fields;
-            foreach (QueryField field in fields)
-                foreach (string alias in field.Aliases)
-                    map[alias.ToLower ()] = field;
+            if (Column.IndexOf ("{0}") == -1)
+                return String.Format ("{0} {1}", Column, String.Format (format, args));
+            else
+                return String.Format (Column, String.Format (format, args));
         }
 
-        public QueryField [] Fields {
-            get { return fields; }
-        }
+        public static string ToTermString (string alias, string op, string value)
+        {
+            value = String.Format (
+                "{1}{0}{1}", 
+                value, value.IndexOf (" ") == -1 ? String.Empty : "\""
+            );
 
-        public Dictionary<string, QueryField> Map {
-            get { return map; }
+            return String.IsNullOrEmpty (alias)
+                ? value
+                : String.Format ("{0}{1}{2}", alias, op, value);
         }
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs	Thu Jan 17 22:25:48 2008
@@ -155,7 +155,7 @@
             return this;
         }
 
-        public override void AppendXml (XmlDocument doc, XmlNode parent)
+        public override void AppendXml (XmlDocument doc, XmlNode parent, QueryFieldSet fieldSet)
         {
             if (ChildCount == 0)
                 return;
@@ -163,12 +163,7 @@
             XmlElement node = doc.CreateElement (Keyword.ToString ().ToLower ());
             parent.AppendChild (node);
             foreach (QueryNode child in Children)
-                child.AppendXml (doc, node);
-        }
-
-        public override string ToString()
-        {
-            return String.Format("<{0}>", Keyword);
+                child.AppendXml (doc, node, fieldSet);
         }
 
         public override void AppendUserQuery (StringBuilder sb)

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs	Thu Jan 17 22:25:48 2008
@@ -76,7 +76,7 @@
 
         public abstract void AppendUserQuery (StringBuilder sb);
 
-        public virtual string ToXml ()
+        public virtual string ToXml (QueryFieldSet fieldSet)
         {
             XmlDocument doc = new XmlDocument ();
 
@@ -87,11 +87,16 @@
             query.SetAttribute ("banshee-version", "1");
             request.AppendChild (query);
 
-            AppendXml (doc, query);
+            AppendXml (doc, query, fieldSet);
             return doc.OuterXml;
         }
 
-        public abstract void AppendXml (XmlDocument doc, XmlNode parent);
+        public override string ToString ()
+        {
+            return ToUserQuery ();
+        }
+
+        public abstract void AppendXml (XmlDocument doc, XmlNode parent, QueryFieldSet fieldSet);
 
         public virtual string ToSql (QueryFieldSet fieldSet)
         {

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs	Thu Jan 17 22:25:48 2008
@@ -49,7 +49,9 @@
             Add (new Operator ("equals", "=="));
             Add (new Operator ("lessThanEquals", "<="));
             Add (new Operator ("greaterThanEquals", ">="));
+            //Add (new Operator ("notEqual", "!="));
             Add (new Operator ("startsWith", "="));
+            //Add (new Operator ("doesNotContain", "!:"));
             Add (new Operator ("contains", ":"));
             Add (new Operator ("lessThan", "<"));
             Add (new Operator ("greaterThan", ">"));
@@ -76,6 +78,10 @@
             return (by_name.ContainsKey (name)) ? by_name [name] : null;
         }
 
+        public static Operator DefaultOperator {
+            get { return Operator.GetByUserOperator (":"); }
+        }
+
         public Operator (string name, string userOp)
         {
             Name = name;
@@ -91,11 +97,13 @@
 
         //private static string [] operators = new string [] {":", "==", "<=", ">=", "=", "<", ">"};
 
-        public QueryTermNode() : base ()
+        public QueryTermNode () : base ()
         {
+            // Set default operator
+            op = Operator.DefaultOperator;
         }
 
-        public QueryTermNode(string value) : base()
+        public QueryTermNode (string value) : base ()
         {
             int field_separator = 0;
             foreach (Operator op in Operator.Operators) {
@@ -106,12 +114,12 @@
                 }
             }
 
-            if(field_separator > 0) {
-                field = value.Substring(0, field_separator);
-                this.field_value = value.Substring(field_separator + op.UserOperator.Length);
+            if (field_separator > 0) {
+                Field = value.Substring (0, field_separator);
+                Value = value.Substring (field_separator + op.UserOperator.Length);
             } else {
                 this.field_value = value;
-                this.op = Operator.GetByUserOperator (":");
+                this.op = Operator.DefaultOperator;
             }
         }
 
@@ -122,31 +130,23 @@
             return this;
         }
         
-        public override string ToString()
-        {
-            if(field != null) {
-                return String.Format("[{0}]=\"{1}\"", field, field_value);
-            } else {
-                return String.Format("\"{0}\"", Value);
-            }
-        }
-
         public override void AppendUserQuery (StringBuilder sb)
         {
-            if (Field != null)
-                sb.AppendFormat ("{0}{1}", Field, Operator.UserOperator);
-            sb.Append (Value);
+            sb.Append (
+                QueryField.ToTermString (Field, Operator.UserOperator, Value)
+            );
         }
 
-        public override void AppendXml (XmlDocument doc, XmlNode parent)
+        public override void AppendXml (XmlDocument doc, XmlNode parent, QueryFieldSet fieldSet)
         {
             XmlElement op_node = doc.CreateElement (op.Name);
             parent.AppendChild (op_node);
 
-            if (Field != null) {
-                XmlElement field = doc.CreateElement ("field");
-                field.SetAttribute ("name", Field);
-                op_node.AppendChild (field);
+            QueryField field = fieldSet.GetByAlias (Field);
+            if (field != null) {
+                XmlElement field_node = doc.CreateElement ("field");
+                field_node.SetAttribute ("name", field.Name);
+                op_node.AppendChild (field_node);
             }
 
             XmlElement val_node = doc.CreateElement ("string");
@@ -158,35 +158,31 @@
         {
             string alias = Field;
             
-            if(alias != null) {
-                alias = alias.ToLower();
-            }
-            
-            if(alias == null || !fieldSet.Map.ContainsKey(alias)) {
+            if (fieldSet.GetByAlias (alias) == null) {
                 sb.Append ("(");
                 int emitted = 0, i = 0;
                 
-                foreach(QueryField field in fieldSet.Fields) {
+                foreach (QueryField field in fieldSet.Fields) {
                     if (field.Default)
                         if (EmitTermMatch (sb, field, emitted > 0))
                             emitted++;
                 }
                 
                 sb.Append (")");
-            } else if(alias != null && fieldSet.Map.ContainsKey(alias)) {
-                EmitTermMatch (sb, fieldSet.Map[alias], false);
+            } else {
+                EmitTermMatch (sb, fieldSet.GetByAlias (alias), false);
             }
         }
 
         private bool EmitTermMatch (StringBuilder sb, QueryField field, bool emit_or)
         {
             if (field.QueryFieldType == QueryFieldType.Text)
-                return EmitStringMatch(sb, field, emit_or);
+                return EmitStringMatch (sb, field, emit_or);
             else
-                return EmitNumericMatch(sb, field, emit_or);
+                return EmitNumericMatch (sb, field, emit_or);
         }
 
-        private bool EmitStringMatch(StringBuilder sb, QueryField field, bool emit_or)
+        private bool EmitStringMatch (StringBuilder sb, QueryField field, bool emit_or)
         {
             string safe_value = (field.Modifier == null ? Value : field.Modifier (Value)).Replace("'", "''");
             
@@ -195,23 +191,31 @@
 
             switch (Operator.UserOperator) {
                 case "=": // Starts with
-                    sb.AppendFormat("{0} LIKE '{1}%'", field.Column, safe_value);
+                    sb.Append (field.FormatSql ("LIKE '{0}%'", safe_value));
                     break;
                     
                 case "==": // Equal to
-                    sb.AppendFormat("{0} LIKE '{1}'", field.Column, safe_value);
+                    sb.Append (field.FormatSql ("= '{0}'", safe_value));
+                    break;
+
+                case "!=": // Not equal to
+                    sb.Append (field.FormatSql ("!= '{0}'", safe_value));
+                    break;
+
+                case "!:": // Doesn't contain
+                    sb.Append (field.FormatSql ("NOT LIKE '%{0}%'", safe_value));
                     break;
 
                 case ":": // Contains
                 default:
-                    sb.AppendFormat("{0} LIKE '%{1}%'", field.Column, safe_value);
+                    sb.Append (field.FormatSql ("LIKE '%{0}%'", safe_value));
                     break;
             }
 
             return true;
         }
 
-        private bool EmitNumericMatch(StringBuilder sb, QueryField field, bool emit_or)
+        private bool EmitNumericMatch (StringBuilder sb, QueryField field, bool emit_or)
         {
             long num = 0;
             
@@ -227,20 +231,20 @@
                 case ":":
                 case "=":
                 case "==":
-                    sb.AppendFormat("{0} = {1}", field.Column, num);
+                    sb.Append (field.FormatSql ("= {0}", num));
                     break;
 
                 default:
-                    sb.AppendFormat("{0} {2} {1}", field.Column, num, Operator.UserOperator);
+                    sb.Append (field.FormatSql ("{1} {0}", num, Operator.UserOperator));
                     break;
             }
             
             return true;
         }
         
-        public string Value {
-            get { return field_value; }
-            set { field_value = value; }
+        public string Field {
+            get { return field; }
+            set { field = value; }
         }
 
         public Operator Operator {
@@ -248,9 +252,9 @@
             set { op = value; }
         }
         
-        public string Field {
-            get { return field; }
-            set { field = value; }
+        public string Value {
+            get { return field_value; }
+            set { field_value = value; }
         }
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Hyena/Makefile.am	(original)
+++ trunk/banshee/src/Core/Hyena/Makefile.am	Thu Jan 17 22:25:48 2008
@@ -13,6 +13,7 @@
 	Hyena.CommandLine/LayoutGroupAttribute.cs \
 	Hyena.CommandLine/LayoutOption.cs \
 	Hyena.Data.Query/QueryField.cs \
+	Hyena.Data.Query/QueryFieldSet.cs \
 	Hyena.Data.Query/QueryListNode.cs \
 	Hyena.Data.Query/QueryNode.cs \
 	Hyena.Data.Query/QueryParser.cs \



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