banshee r3912 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Playlist src/Core/Banshee.Services/Banshee.Query src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.Services/Banshee.Sources src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Core/Banshee.Widgets/Banshee.Widgets src/Extensions/Banshee.AudioCd/Banshee.AudioCd src/Libraries/Hyena/Hyena.Data.Sqlite src/Libraries/Hyena/Hyena.Query



Author: gburt
Date: Fri May  9 18:40:15 2008
New Revision: 3912
URL: http://svn.gnome.org/viewvc/banshee?rev=3912&view=rev

Log:
2008-05-09  Gabriel Burt  <gabriel burt gmail com>

	This commit greatly improves performance when editing/saving track
	information and on each track transition by avoiding many unnecessary
	source reloads.

	* src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs: Improve
	auto-rip label.

	* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs:
	Avoid changing to 'Idle' when transitioning between songs, and avoid
	jumpiness by not setting the duration until the first Iteration event.
	Makes things much smoother.

	* src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs: Fix the
	horrible save performance by avoiding reloading until after having saved
	all the tracks, instead of after each one.  Fix whitespace.

	* src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs: Change
	OnTracksChanged to take an array of QueryFields that were modified.  Add
	protected InvalidateCaches method used to clear the managed cache for the
	track/artist/album model, but not reload it (eg the display data has
	changed, but sort order/contents have not).

	* src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs: Change
	TracksChanged to QueryField array.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseAlbumListModel.cs:
	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseArtistListModel.cs:
	Remove ClearCache method, and add InvalidateCache method.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs:
	Specify which fields are updated in the IncrementPlay/SkipCount methods to
	allow sources to optimize whether they need to reload b/c of the change.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs:
	Add InvalidateCache method, and expose the currently parsed QueryNode, if
	any.

	* src/Core/Banshee.Services/Banshee.Collection.Database/IDatabaseTrackModelCache.cs:
	* src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs:
	Add ClearManagedCache method.

	* src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs:
	* src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs:
	* src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs:
	In TrackChanged handler, only Reload if we are actually affected by the
	change - so if the change was play count, and we're not sorted by it,
	querying on it, or if we're a smart playlist, defined by it, then just
	InvalidateCaches.

	* src/Libraries/Hyena/Hyena.Query/QueryOrder.cs:
	* src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs: Associate a
	QueryField with QueryOrders.

	* src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs: Avoid
	updating twice as often as needed.  Use String.Format instead of +.

	* src/Libraries/Hyena/Hyena.Query/QueryNode.cs: Add GetTerms and GetFields
	convenience methods.


Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseAlbumListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseArtistListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/IDatabaseTrackModelCache.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Query/BansheeQuery.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs
   trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryNode.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOrder.cs

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseAlbumListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseAlbumListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseAlbumListModel.cs	Fri May  9 18:40:15 2008
@@ -165,9 +165,10 @@
             get { return (int) cache.CacheId; }
         }
 
-        public void ClearCache ()
+        public void InvalidateCache ()
         {
-            cache.Clear ();
+            cache.ClearManagedCache ();
+            OnReloaded ();
         }
 
         public string JoinTable { get { return null; } }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseArtistListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseArtistListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseArtistListModel.cs	Fri May  9 18:40:15 2008
@@ -118,9 +118,10 @@
             get { return (int) cache.CacheId; }
         }
 
-        public void ClearCache ()
+        public void InvalidateCache ()
         {
-            cache.Clear ();
+            cache.ClearManagedCache ();
+            OnReloaded ();
         }
 
         // Implement ICacheableModel

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs	Fri May  9 18:40:15 2008
@@ -32,10 +32,12 @@
 
 using Hyena;
 using Hyena.Data.Sqlite;
+using Hyena.Query;
 
 using Banshee.Base;
 using Banshee.Configuration.Schema;
 using Banshee.Database;
+using Banshee.Query;
 using Banshee.Sources;
 using Banshee.ServiceStack;
 using Banshee.Streaming;
@@ -82,7 +84,7 @@
         {
             if (Provider.Refresh (this)) {
                 base.IncrementPlayCount ();
-                Save ();
+                Save (true, BansheeQuery.PlayCountField, BansheeQuery.LastPlayedField);
             }
         }
 
@@ -90,7 +92,7 @@
         {
             if (Provider.Refresh (this)) {
                 base.IncrementSkipCount ();
-                Save ();
+                Save (true, BansheeQuery.SkipCountField, BansheeQuery.LastSkippedField);
             }
         }
 
@@ -105,11 +107,6 @@
                 db_track.CacheEntryId == CacheEntryId;
         }
         
-        public override void Save ()
-        {
-            Save (true);
-        }
-        
         public DatabaseArtistInfo Artist {
             get { return DatabaseArtistInfo.FindOrCreate (ArtistName); }
         }
@@ -118,7 +115,18 @@
             get { return DatabaseAlbumInfo.FindOrCreate (Artist, AlbumTitle); }
         }
 
-        public void Save (bool notify)
+        private static bool notify_saved = true;
+        public static bool NotifySaved {
+            get { return notify_saved; }
+            set { notify_saved = value; }
+        }
+
+        public override void Save ()
+        {
+            Save (NotifySaved);
+        }
+
+        public void Save (bool notify, params QueryField [] fields_changed)
         {
             // If either the artist or album changed, 
             if (ArtistId == 0 || AlbumId == 0 || artist_changed == true || album_changed == true) {
@@ -142,7 +150,7 @@
                 if (is_new) {
                     PrimarySource.NotifyTracksAdded ();
                 } else {
-                    PrimarySource.NotifyTracksChanged ();
+                    PrimarySource.NotifyTracksChanged (fields_changed);
                 }
             }
         }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs	Fri May  9 18:40:15 2008
@@ -116,18 +116,25 @@
 
             if (String.IsNullOrEmpty (Filter)) {
                 query_fragment = null;
+                query_tree = null;
             } else {
-                QueryNode query_tree = UserQueryParser.Parse (Filter, BansheeQuery.FieldSet);
+                query_tree = UserQueryParser.Parse (Filter, BansheeQuery.FieldSet);
                 query_fragment = (query_tree == null) ? null : query_tree.ToSql (BansheeQuery.FieldSet);
 
                 if (query_fragment != null && query_fragment.Length == 0) {
                     query_fragment = null;
+                    query_tree = null;
                 }
             }
 
             have_new_filter = false;
         }
 
+        private QueryNode query_tree;
+        public QueryNode Query {
+            get { return query_tree; }
+        }
+
         private void GenerateSortQueryPart ()
         {
             sort_query = (sort_column == null)
@@ -169,6 +176,12 @@
             OnCleared ();
         }
 
+        public void InvalidateCache ()
+        {
+            cache.ClearManagedCache ();
+            OnReloaded ();
+        }
+
         private string unfiltered_query;
         protected string UnfilteredQuery {
             get {

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/IDatabaseTrackModelCache.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/IDatabaseTrackModelCache.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/IDatabaseTrackModelCache.cs	Fri May  9 18:40:15 2008
@@ -36,6 +36,7 @@
     public interface IDatabaseTrackModelCache
     {
         void Clear ();
+        void ClearManagedCache ();
         void SaveSelection ();
         void UpdateAggregates ();
         void RestoreSelection ();

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs	Fri May  9 18:40:15 2008
@@ -37,6 +37,7 @@
 using Hyena.Data;
 using Hyena.Data.Sqlite;
 using Hyena.Collections;
+using Hyena.Query;
 
 using Banshee.Base;
 using Banshee.ServiceStack;
@@ -44,6 +45,7 @@
 using Banshee.Sources;
 using Banshee.Collection;
 using Banshee.Collection.Database;
+using Banshee.Query;
 
 namespace Banshee.Playlist
 {
@@ -176,6 +178,55 @@
             get { return (Parent is DatabaseSource) ? (Parent as DatabaseSource).ShowBrowser : base.ShowBrowser; }
         }
 
+        protected bool NeedsReloadWhenFieldsChanged (Hyena.Query.QueryField [] fields)
+        {
+            if (fields == null) {
+                return true;
+            }
+
+            foreach (QueryField field in fields)
+                if (NeedsReloadWhenFieldChanged (field))
+                    return true;
+
+            return false;
+        }
+
+        private List<QueryField> query_fields;
+        private QueryNode last_query;
+        protected virtual bool NeedsReloadWhenFieldChanged (Hyena.Query.QueryField field)
+        {
+            if (field == null)
+                return true;
+
+            // If it's the artist or album name, then we care, since it affects the browser
+            if (field == Banshee.Query.BansheeQuery.ArtistField || field == Banshee.Query.BansheeQuery.AlbumField) {
+                return true;
+            }
+
+            ISortableColumn sort_column = (TrackModel is DatabaseTrackListModel)
+                ? (TrackModel as DatabaseTrackListModel).SortColumn : null;
+            // If it's the field we're sorting by, then yes, we care
+            // FIXME this Contains is very hacky, we should link the ISortableColumn to a field and/or a
+            // QueryOrder object
+            if (sort_column != null && field.Column.Contains (sort_column.SortKey)) {
+                return true;
+            }
+
+            // Make sure the query isn't dependent on this field
+            QueryNode query = (TrackModel is DatabaseTrackListModel)
+                ? (TrackModel as DatabaseTrackListModel).Query : null;
+            if (query != null) {
+                if (query != last_query) {
+                    query_fields = new List<QueryField> (query.GetFields ());
+                }
+
+                if (query_fields.Contains (field))
+                    return true;
+            }
+
+            return false;
+        }
+
         protected abstract void Create ();
         protected abstract void Update ();
     }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs	Fri May  9 18:40:15 2008
@@ -259,12 +259,15 @@
         {
             if (args.When > last_updated) {
                 last_updated = args.When;
-                // Optimization: playlists only need to reload for updates if they are sorted by the column updated
-                //if (args.ChangedField == null || args.ChangedChanged == [current sort field]) {
+                // Playlists do not need to reload if only certain columns are changed
+                if (NeedsReloadWhenFieldsChanged (args.ChangedFields)) {
+                    // TODO Optimization: playlists only need to reload if one of their tracks was updated
                     //if (ServiceManager.DbConnection.Query<int> (count_updated_command, last_updated) > 0) {
                         Reload ();
                     //}
-                //}
+                } else {
+                    InvalidateCaches ();
+                }
             }
         }
 

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	Fri May  9 18:40:15 2008
@@ -43,26 +43,26 @@
         private static bool asc = true;
         private static bool desc = false;
 
-        public static QueryOrder RandomOrder = CreateQueryOrder ("Random",     asc,  Catalog.GetString ("Random"));
+        public static QueryOrder RandomOrder = CreateQueryOrder ("Random",     asc,  Catalog.GetString ("Random"), null);
 
         public static QueryOrder [] Orders = new QueryOrder [] {
             RandomOrder,
-            CreateQueryOrder ("Album",      asc,  Catalog.GetString ("Album")),
-            CreateQueryOrder ("Artist",     asc,  Catalog.GetString ("Artist")),
-            CreateQueryOrder ("Title",      asc,  Catalog.GetString ("Title")),
-            CreateQueryOrder ("Genre",      asc,  Catalog.GetString ("Genre")),
+            CreateQueryOrder ("Album",      asc,  Catalog.GetString ("Album"), AlbumField),
+            CreateQueryOrder ("Artist",     asc,  Catalog.GetString ("Artist"), ArtistField),
+            CreateQueryOrder ("Title",      asc,  Catalog.GetString ("Title"), TitleField),
+            CreateQueryOrder ("Genre",      asc,  Catalog.GetString ("Genre"), GenreField),
             null,
-            CreateQueryOrder ("Rating",     desc, Catalog.GetString ("Highest Rating")),
-            CreateQueryOrder ("Rating",     asc,  Catalog.GetString ("Lowest Rating")),
+            CreateQueryOrder ("Rating",     desc, Catalog.GetString ("Highest Rating"), RatingField),
+            CreateQueryOrder ("Rating",     asc,  Catalog.GetString ("Lowest Rating"), RatingField),
             null,
-            CreateQueryOrder ("PlayCount",  desc, Catalog.GetString ("Most Often Played")),
-            CreateQueryOrder ("PlayCount",  asc,  Catalog.GetString ("Least Often Played")),
+            CreateQueryOrder ("PlayCount",  desc, Catalog.GetString ("Most Often Played"), PlayCountField),
+            CreateQueryOrder ("PlayCount",  asc,  Catalog.GetString ("Least Often Played"), PlayCountField),
             null,
-            CreateQueryOrder ("LastPlayedStamp", desc, Catalog.GetString ("Most Recently Played")),
-            CreateQueryOrder ("LastPlayedStamp", asc,  Catalog.GetString ("Least Recently Played")),
+            CreateQueryOrder ("LastPlayedStamp", desc, Catalog.GetString ("Most Recently Played"), LastPlayedField),
+            CreateQueryOrder ("LastPlayedStamp", asc,  Catalog.GetString ("Least Recently Played"), LastPlayedField),
             null,
-            CreateQueryOrder ("DateAddedStamp",  desc, Catalog.GetString ("Most Recently Added")),
-            CreateQueryOrder ("DateAddedStamp",  asc,  Catalog.GetString ("Least Recently Added"))
+            CreateQueryOrder ("DateAddedStamp",  desc, Catalog.GetString ("Most Recently Added"), DateAddedField),
+            CreateQueryOrder ("DateAddedStamp",  asc,  Catalog.GetString ("Least Recently Added"), DateAddedField)
         };
 
         public static QueryLimit [] Limits = new QueryLimit [] {
@@ -276,9 +276,9 @@
             return sort_query;
         }
 
-        private static QueryOrder CreateQueryOrder (string name, bool asc, string label)
+        private static QueryOrder CreateQueryOrder (string name, bool asc, string label, QueryField field)
         {
-            return new QueryOrder (CreateOrderName (name, asc), label, GetSort (name, asc));
+            return new QueryOrder (CreateOrderName (name, asc), label, GetSort (name, asc), field);
         }
 
         public static QueryLimit FindLimit (string name)

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	Fri May  9 18:40:15 2008
@@ -106,14 +106,21 @@
         }
 
         // Custom properties
+        private List<QueryField> relevant_fields = new List<QueryField> ();
         private QueryNode condition;
         public QueryNode ConditionTree {
             get { return condition; }
             set {
                 condition = value;
+                relevant_fields.Clear ();
                 if (condition != null) {
                     condition_sql = condition.ToSql (BansheeQuery.FieldSet);
                     condition_xml = condition.ToXml (BansheeQuery.FieldSet);
+                    
+                    foreach (QueryTermNode term in condition.GetTerms ()) {
+                        if (!relevant_fields.Contains (term.Field))
+                            relevant_fields.Add (term.Field);
+                    }
                 }
             }
         }
@@ -299,6 +306,20 @@
             UpdateDependencies ();
         }
 
+        protected override bool NeedsReloadWhenFieldChanged (Hyena.Query.QueryField field)
+        {
+            if (base.NeedsReloadWhenFieldChanged (field))
+                return true;
+
+            if (QueryOrder != null && QueryOrder.Field == field)
+                return true;
+
+            if (relevant_fields.Contains (field))
+                return true;
+
+            return false;
+        }
+
 #endregion
 
 #region DatabaseSource overrides
@@ -411,8 +432,12 @@
         protected override void HandleTracksChanged (Source sender, TrackEventArgs args)
         {
             if (args.When > last_updated) {
-                last_updated = args.When;
-                RefreshAndReload ();
+                if (NeedsReloadWhenFieldsChanged (args.ChangedFields)) {
+                    last_updated = args.When;
+                    RefreshAndReload ();
+                } else {
+                    InvalidateCaches ();
+                }
             }
         }
 
@@ -421,7 +446,6 @@
             if (args.When > last_removed) {
                 last_removed = args.When;
                 RefreshAndReload ();
-                Reload ();
                 /*if (ServiceManager.DbConnection.Query<int> (count_removed_command, last_removed) > 0) {
                     if (Limit == null) {
                         //track_model.UpdateAggregates ();

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs	Fri May  9 18:40:15 2008
@@ -209,9 +209,9 @@
         {
             lock (track_model) {
                 DatabaseTrackModel.Reload ();
-                OnUpdated ();
-                Save ();
             }
+            OnUpdated ();
+            Save ();
         }
 
         public virtual bool HasDependencies {
@@ -328,11 +328,11 @@
             OnTracksChanged (null);
         }
 
-        protected virtual void OnTracksChanged (QueryField field)
+        protected virtual void OnTracksChanged (params QueryField [] fields)
         {
-            HandleTracksChanged (this, new TrackEventArgs (field));
+            HandleTracksChanged (this, new TrackEventArgs (fields));
             foreach (PrimarySource psource in PrimarySources) {
-                psource.NotifyTracksChanged (field);
+                psource.NotifyTracksChanged (fields);
             }
         }
 
@@ -462,6 +462,17 @@
             }
         }
 
+        protected void InvalidateCaches ()
+        {
+            track_model.InvalidateCache ();
+
+            if (artist_model != null)
+                artist_model.InvalidateCache ();
+
+            if (album_model != null)
+                album_model.InvalidateCache ();
+        }
+
         protected virtual void PruneArtistsAlbums ()
         {
             //Console.WriteLine ("Pruning with {0}", PruneCommand.Text);

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs	Fri May  9 18:40:15 2008
@@ -55,9 +55,9 @@
             get { return when; }
         }
 
-        private QueryField changed_field;
-        public QueryField ChangedField {
-            get { return changed_field; }
+        private QueryField [] changed_fields;
+        public QueryField [] ChangedFields {
+            get { return changed_fields; }
         }
 
         public TrackEventArgs ()
@@ -65,9 +65,9 @@
             when = DateTime.Now;
         }
 
-        public TrackEventArgs (QueryField field) : this ()
+        public TrackEventArgs (params QueryField [] fields) : this ()
         {
-            this.changed_field = field;
+            changed_fields = fields;
         }
     }
 
@@ -245,12 +245,13 @@
             OnTracksAdded ();
         }
 
-        internal void NotifyTracksChanged (QueryField field)
+        internal void NotifyTracksChanged (params QueryField [] fields)
         {
-            OnTracksChanged (field);
+            OnTracksChanged (fields);
         }
 
-        internal void NotifyTracksChanged ()
+        // TODO replace this public method with a 'transaction'-like system
+        public void NotifyTracksChanged ()
         {
             OnTracksChanged ();
         }
@@ -314,14 +315,14 @@
             });
         }
 
-        protected override void OnTracksChanged (QueryField field)
+        protected override void OnTracksChanged (params QueryField [] fields)
         {
             ThreadAssist.SpawnFromMain (delegate {
                 Reload ();
 
                 TrackEventHandler handler = TracksChanged;
                 if (handler != null) {
-                    handler (this, new TrackEventArgs (field));
+                    handler (this, new TrackEventArgs (fields));
                 }
             });
         }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs	Fri May  9 18:40:15 2008
@@ -74,18 +74,18 @@
         public int SampleRate;
         public long FileSize;
     
-        public EditorTrack(TrackInfo track)
+        public EditorTrack (TrackInfo track)
         {
             this.track = track;
-            Revert();
+            Revert ();
         }
         
-        public void Revert()
+        public void Revert ()
         {
-            ArtistName = track.ArtistName == null ? String.Empty : track.ArtistName;
-            AlbumTitle = track.AlbumTitle == null ? String.Empty : track.AlbumTitle;
-            TrackTitle = track.TrackTitle == null ? String.Empty : track.TrackTitle;
-            Genre = track.Genre == null ? String.Empty : track.Genre;
+            ArtistName = track.ArtistName ?? String.Empty;
+            AlbumTitle = track.AlbumTitle ?? String.Empty;
+            TrackTitle = track.TrackTitle ?? String.Empty;
+            Genre = track.Genre ?? String.Empty;
             Disc = track.Disc;
             TrackNumber = track.TrackNumber;
             TrackCount = track.TrackCount;
@@ -94,7 +94,7 @@
             Rating = track.Rating;
         }
         
-        public void Save()
+        public void Save ()
         {
             track.ArtistName = ArtistName;
             track.AlbumTitle = AlbumTitle;
@@ -152,30 +152,30 @@
         [Widget] private CheckButton CopyCoverArt;
         [Widget] private Box RatingContainer;
         
-        private Tooltips tips = new Tooltips();
-        private RatingEntry rating_entry = new RatingEntry();
-        private List<EntryUndoAdapter> entry_undo_adapters = new List<EntryUndoAdapter>();
+        private Tooltips tips = new Tooltips ();
+        private RatingEntry rating_entry = new RatingEntry ();
+        private List<EntryUndoAdapter> entry_undo_adapters = new List<EntryUndoAdapter> ();
         
-        private List<EditorTrack> TrackSet = new List<EditorTrack>();
+        private List<EditorTrack> TrackSet = new List<EditorTrack> ();
         private int currentIndex = 0;
         private bool first_load = false;
 
         public event EventHandler Saved;
 
-        public TrackEditor(IEnumerable<TrackInfo> selection) : base("TrackEditorWindow")
+        public TrackEditor (IEnumerable<TrackInfo> selection) : base ("TrackEditorWindow")
         {
-            if(selection == null) {
+            if (selection == null) {
                 return;
             }
         
-            foreach(TrackInfo track in selection) {
+            foreach (TrackInfo track in selection) {
                 if (track != null)
-                    TrackSet.Add(new EditorTrack(track));
+                    TrackSet.Add (new EditorTrack (track));
             }
             
-            rating_entry.Show();
+            rating_entry.Show ();
             (Glade["RatingLabel"] as Label).MnemonicWidget = rating_entry;
-            RatingContainer.PackStart(rating_entry, false, false, 0);
+            RatingContainer.PackStart (rating_entry, false, false, 0);
 
             TrackNumberIterator.ExposeEvent += OnTrackNumberIteratorExpose;
 
@@ -204,13 +204,13 @@
             Genre.Entry.Changed += OnValueEdited;
             rating_entry.Changed += OnValueEdited;
             
-            ListStore genre_model = new ListStore(typeof(string));
+            ListStore genre_model = new ListStore (typeof (string));
             Genre.Model = genre_model;
             Genre.TextColumn = 0;
             
             // FIXME merge
-            /*foreach(string genre in Globals.Library.GetGenreList()) {
-                genre_model.AppendValues(genre);            
+            /*foreach (string genre in Globals.Library.GetGenreList ()) {
+                genre_model.AppendValues (genre);            
             }*/
             
             Next.Visible = TrackSet.Count > 1;
@@ -228,35 +228,35 @@
             EnterNextTitle.Visible = TrackSet.Count > 1;
             Glade["SyncAllAlignment"].Visible = TrackSet.Count > 1;
             
-            EditorNotebook.RemovePage(1);
+            EditorNotebook.RemovePage (1);
 
-            tips.SetTip(TrackNumberIterator, Catalog.GetString("Automatically set all track numbers in increasing order"), "track iterator");
-            tips.SetTip(TrackCountSync, Catalog.GetString("Set all track counts to this value"), "track counts");
-            tips.SetTip(ArtistSync, Catalog.GetString("Set all artists to this value"), "artists");
-            tips.SetTip(AlbumSync, Catalog.GetString("Set all albums to this value"), "albums");
-            tips.SetTip(DiscSync, Catalog.GetString("Set all disc numbers to this value"), "discs");
-            tips.SetTip(GenreSync, Catalog.GetString("Set all genres to this value"), "genres"); 
-            tips.SetTip(YearSync, Catalog.GetString("Set all years to this value"), "years");
-            tips.SetTip(SyncAll, Catalog.GetString("Apply the values of this track set for the Artist, Album Title, Genre, Track count, Year, and Rating fields to the rest of the selected tracks in this editor."), "all");
-            tips.SetTip(RatingSync, Catalog.GetString("Set all ratings to this value"), "ratings");
+            tips.SetTip (TrackNumberIterator, Catalog.GetString ("Automatically set all track numbers in increasing order"), "track iterator");
+            tips.SetTip (TrackCountSync, Catalog.GetString ("Set all track counts to this value"), "track counts");
+            tips.SetTip (ArtistSync, Catalog.GetString ("Set all artists to this value"), "artists");
+            tips.SetTip (AlbumSync, Catalog.GetString ("Set all albums to this value"), "albums");
+            tips.SetTip (DiscSync, Catalog.GetString ("Set all disc numbers to this value"), "discs");
+            tips.SetTip (GenreSync, Catalog.GetString ("Set all genres to this value"), "genres"); 
+            tips.SetTip (YearSync, Catalog.GetString ("Set all years to this value"), "years");
+            tips.SetTip (SyncAll, Catalog.GetString ("Apply the values of this track set for the Artist, Album Title, Genre, Track count, Year, and Rating fields to the rest of the selected tracks in this editor."), "all");
+            tips.SetTip (RatingSync, Catalog.GetString ("Set all ratings to this value"), "ratings");
             
-            LoadTrack(0);
+            LoadTrack (0);
             
-            foreach(Entry entry in new Entry [] { Artist, Album, Title, Year, Genre.Entry }) {
-                entry_undo_adapters.Add(new EntryUndoAdapter(entry));
+            foreach (Entry entry in new Entry [] { Artist, Album, Title, Year, Genre.Entry }) {
+                entry_undo_adapters.Add (new EntryUndoAdapter (entry));
             }
             
-            Window.Show();
+            Window.Show ();
         }
         
-        private void OnTrackNumberIteratorExpose(object o, ExposeEventArgs args)
+        private void OnTrackNumberIteratorExpose (object o, ExposeEventArgs args)
         {
             Gdk.Rectangle alloc = TrackNumberIterator.Allocation;
-            Gdk.GC gc = TrackNumberIterator.Style.DarkGC(StateType.Normal);
+            Gdk.GC gc = TrackNumberIterator.Style.DarkGC (StateType.Normal);
             Gdk.Drawable drawable = TrackNumberIterator.GdkWindow;
             
-            int x_pad = (int)((double)alloc.Width * 0.15);
-            int y_pad = (int)((double)alloc.Height * 0.15);
+            int x_pad = (int) ((double)alloc.Width * 0.15);
+            int y_pad = (int) ((double)alloc.Height * 0.15);
             
             int left_x = alloc.X + x_pad;
             int top_y = alloc.Y + y_pad;
@@ -264,23 +264,23 @@
             int mid_y = alloc.Y + (alloc.Height / 2);
             int bottom_y = (alloc.Y + alloc.Height) - y_pad;
             
-            drawable.DrawLine(gc, left_x, top_y, mid_x, top_y);
-            drawable.DrawLine(gc, mid_x + 1, top_y + 1, mid_x + 1, mid_y - 15);
+            drawable.DrawLine (gc, left_x, top_y, mid_x, top_y);
+            drawable.DrawLine (gc, mid_x + 1, top_y + 1, mid_x + 1, mid_y - 15);
             
-            drawable.DrawLine(gc, left_x, bottom_y, mid_x, bottom_y);
-            drawable.DrawLine(gc, mid_x + 1, bottom_y - 1, mid_x + 1, mid_y + 13);
+            drawable.DrawLine (gc, left_x, bottom_y, mid_x, bottom_y);
+            drawable.DrawLine (gc, mid_x + 1, bottom_y - 1, mid_x + 1, mid_y + 13);
         }
         
-        private void LoadTrack(int index)
+        private void LoadTrack (int index)
         {
-            if(index < 0 || index >= TrackSet.Count) {
+            if (index < 0 || index >= TrackSet.Count) {
                 return;
             }
                 
-            SetCoverImage(null);
+            SetCoverImage (null);
             
-            foreach(EntryUndoAdapter undo_adapter in entry_undo_adapters) {
-                undo_adapter.UndoManager.Clear();
+            foreach (EntryUndoAdapter undo_adapter in entry_undo_adapters) {
+                undo_adapter.UndoManager.Clear ();
             }
                 
             EditorTrack track = TrackSet[index] as EditorTrack;
@@ -288,7 +288,7 @@
             TrackNumber.Value = track.TrackNumber;
             TrackCount.Value = track.TrackCount;
             Disc.Value = track.Disc;
-            Year.Text = track.Year.ToString();
+            Year.Text = track.Year.ToString ();
             rating_entry.Value = (int)track.Rating;
         
             (Glade["Artist"] as Entry).Text = track.ArtistName;
@@ -296,15 +296,15 @@
             (Glade["Title"] as Entry).Text = track.TrackTitle;
             (Glade["Genre"] as ComboBoxEntry).Entry.Text = track.Genre;
             
-            (Glade["DurationLabel"] as Label).Text = String.Format("{0}:{1}", 
-                track.Track.Duration.Minutes, (track.Track.Duration.Seconds).ToString("00"));
-            (Glade["PlayCountLabel"] as Label).Text = track.Track.PlayCount.ToString();
+            (Glade["DurationLabel"] as Label).Text = String.Format ("{0}:{1}", 
+                track.Track.Duration.Minutes, (track.Track.Duration.Seconds).ToString ("00"));
+            (Glade["PlayCountLabel"] as Label).Text = track.Track.PlayCount.ToString ();
             (Glade["LastPlayedLabel"] as Label).Text = track.Track.LastPlayed == DateTime.MinValue ?
-                Catalog.GetString("Never played") : track.Track.LastPlayed.ToString();
+                Catalog.GetString ("Never played") : track.Track.LastPlayed.ToString ();
             (Glade["ImportedLabel"] as Label).Text = track.Track.DateAdded == DateTime.MinValue ?
-                Catalog.GetString("Unknown") : track.Track.DateAdded.ToString();
+                Catalog.GetString ("Unknown") : track.Track.DateAdded.ToString ();
                 
-            if(first_load) {
+            if (first_load) {
                 EmbedCoverArt.Active = true;
                 CopyCoverArt.Active = true;
                 first_load = false;
@@ -314,34 +314,35 @@
             }
             
             Window.Title = TrackSet.Count > 1 
-                ? String.Format(Catalog.GetString("Editing item {0} of {1}"), index + 1, TrackSet.Count)
-                : String.Format(Catalog.GetString("Editing {0}"), track.TrackTitle);
+                ? String.Format (Catalog.GetString ("Editing item {0} of {1}"), index + 1, TrackSet.Count)
+                : String.Format (Catalog.GetString ("Editing {0}"), track.TrackTitle);
        
-            if(track.Uri.IsLocalPath) {
-                Uri.Text = System.IO.Path.GetFileName(track.Uri.LocalPath);
-                Location.Text = System.IO.Path.GetDirectoryName(track.Uri.LocalPath);
+            if (track.Uri.IsLocalPath) {
+                Uri.Text = System.IO.Path.GetFileName (track.Uri.LocalPath);
+                Location.Text = System.IO.Path.GetDirectoryName (track.Uri.LocalPath);
             } else {
-                Uri.Text = track.Uri.ToString();
+                Uri.Text = track.Uri.ToString ();
                 Location.Text = String.Empty;
             }
             
-            FileSize.Text = Catalog.GetString("Unknown");
+            FileSize.Text = Catalog.GetString ("Unknown");
                 
             // FIXME merge
-            //if(!(track.Track is AudioCdTrackInfo) && !track.ProcessedStream) {
-            if(!track.ProcessedStream) {
+            //if (!(track.Track is AudioCdTrackInfo) && !track.ProcessedStream) {
+            if (!track.ProcessedStream) {
                 track.ProcessedStream = true;
                 
-                if(track.Uri.Scheme == System.Uri.UriSchemeFile) {
+                if (track.Uri.Scheme == System.Uri.UriSchemeFile) {
+                    // TODO have this use the TrackInfo's FileSize
                     try {
-                        System.IO.FileInfo info = new System.IO.FileInfo(track.Uri.LocalPath);
+                        System.IO.FileInfo info = new System.IO.FileInfo (track.Uri.LocalPath);
                         track.FileSize = info.Length;
                     } catch {
                     }
                 }
                 
                 try {
-                    TagLib.File file = StreamTagger.ProcessUri(track.Uri);
+                    TagLib.File file = StreamTagger.ProcessUri (track.Uri);
                 
                     /*try {
                         TagLib.IPicture [] pictures = file.Tag.Pictures;
@@ -370,25 +371,25 @@
                     track.Bitrate = file.Properties.AudioBitrate;
                     track.SampleRate = file.Properties.AudioSampleRate;
                     track.Channels = file.Properties.AudioChannels;
-                } catch(Exception) {
+                } catch (Exception) {
                     track.Bitrate = -1;
                 }
             } 
             
-            if(track.ProcessedStream) {
-                FileSize.Text = String.Format("{0:0.0} MB", (double)track.FileSize / 1024.0 / 1024.0);
+            if (track.ProcessedStream) {
+                FileSize.Text = String.Format ("{0:0.0} MB", (double)track.FileSize / 1024.0 / 1024.0);
                 
-                if(track.Bitrate > 0) {
-                    BitRate.Text = String.Format("{0} kbps", track.Bitrate);
-                    SampleRate.Text = String.Format("{0} Hz", track.SampleRate);
-                    Channels.Text = String.Format("{0}", track.Channels);
+                if (track.Bitrate > 0) {
+                    BitRate.Text = String.Format ("{0} kbps", track.Bitrate);
+                    SampleRate.Text = String.Format ("{0} Hz", track.SampleRate);
+                    Channels.Text = String.Format ("{0}", track.Channels);
                 } else {
-                    BitRate.Text = String.Format("{0} kbps", track.Bitrate);
-                    SampleRate.Text = String.Format("{0} Hz", track.SampleRate);
-                    Channels.Text = String.Format("{0}", track.Channels);
+                    BitRate.Text = String.Format ("{0} kbps", track.Bitrate);
+                    SampleRate.Text = String.Format ("{0} Hz", track.SampleRate);
+                    Channels.Text = String.Format ("{0}", track.Channels);
                 }
                 
-                SetCoverImage(track.CoverArtThumbnail);
+                SetCoverImage (track.CoverArtThumbnail);
             }
             
             Previous.Sensitive = index > 0;
@@ -396,33 +397,33 @@
             EnterNextTitle.Sensitive = Next.Sensitive;
         }
         
-        private static Gdk.Pixbuf no_cover_image = Gdk.Pixbuf.LoadFromResource("browser-album-cover.png");
+        private static Gdk.Pixbuf no_cover_image = Gdk.Pixbuf.LoadFromResource ("browser-album-cover.png");
         
-        private void SetCoverImage(Gdk.Pixbuf pixbuf)
+        private void SetCoverImage (Gdk.Pixbuf pixbuf)
         {
-            if(pixbuf == null) {
+            if (pixbuf == null) {
                 CoverImage.Pixbuf = no_cover_image;
             } else {
                 CoverImage.Pixbuf = pixbuf;
             }
         }
         
-        private void OnPreviousClicked(object o, EventArgs args)
+        private void OnPreviousClicked (object o, EventArgs args)
         {
-            UpdateCurrent();
-            LoadTrack(--currentIndex);
+            UpdateCurrent ();
+            LoadTrack (--currentIndex);
         }
         
-        private void OnNextClicked(object o, EventArgs args)
+        private void OnNextClicked (object o, EventArgs args)
         {
-            UpdateCurrent();
-            LoadTrack(++currentIndex);
+            UpdateCurrent ();
+            LoadTrack (++currentIndex);
         }
 
-        private void OnTrackNumberIteratorClicked(object o, EventArgs args)
+        private void OnTrackNumberIteratorClicked (object o, EventArgs args)
         {
             int i = 1;
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 track.TrackNumber = i++;
                 track.TrackCount = TrackSet.Count;
             }
@@ -432,130 +433,130 @@
             TrackCount.Value = current_track.TrackCount;
         }
         
-        private void OnValueEdited(object o, EventArgs args)
+        private void OnValueEdited (object o, EventArgs args)
         {
-            if(currentIndex < 0 || currentIndex >= TrackSet.Count) {
+            if (currentIndex < 0 || currentIndex >= TrackSet.Count) {
                 return;
             }
         }
         
-        private void OnTrackCountSyncClicked(object o, EventArgs args)
+        private void OnTrackCountSyncClicked (object o, EventArgs args)
         {
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 track.TrackCount = (int)TrackCount.Value;
             }
         }
         
-        private void OnRatingSyncClicked(object o, EventArgs args)
+        private void OnRatingSyncClicked (object o, EventArgs args)
         {
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 track.Rating = rating_entry.Value;
             }
         }
         
-        private void OnYearSyncClicked(object o, EventArgs args)
+        private void OnYearSyncClicked (object o, EventArgs args)
         {
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 try {
-                    track.Year = Convert.ToInt32(Year.Text);
+                    track.Year = Convert.ToInt32 (Year.Text);
                 } catch {
                     track.Year = 0;
                 }
             }
         }
 
-        private void OnDiscSyncClicked(object o, EventArgs args)
+        private void OnDiscSyncClicked (object o, EventArgs args)
         {
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 track.Disc = (int)Disc.Value;
             }
         }
         
-        private void OnArtistSyncClicked(object o, EventArgs args)
+        private void OnArtistSyncClicked (object o, EventArgs args)
         {
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 track.ArtistName = Artist.Text;
             }
         }
 
-        private void OnAlbumSyncClicked(object o, EventArgs args)
+        private void OnAlbumSyncClicked (object o, EventArgs args)
         {
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 track.AlbumTitle = Album.Text;
             }
         }
         
-        private void OnSyncAllClicked(object o, EventArgs args)
+        private void OnSyncAllClicked (object o, EventArgs args)
         {
-            OnTrackCountSyncClicked(o, args);
-            OnGenreSyncClicked(o, args);
-            OnDiscSyncClicked(o, args);
-            OnAlbumSyncClicked(o, args);
-            OnArtistSyncClicked(o, args);
-            OnYearSyncClicked(o, args);
-            OnRatingSyncClicked(o, args);
+            OnTrackCountSyncClicked (o, args);
+            OnGenreSyncClicked (o, args);
+            OnDiscSyncClicked (o, args);
+            OnAlbumSyncClicked (o, args);
+            OnArtistSyncClicked (o, args);
+            OnYearSyncClicked (o, args);
+            OnRatingSyncClicked (o, args);
         }
         
-        private void OnGenreSyncClicked(object o, EventArgs args)
+        private void OnGenreSyncClicked (object o, EventArgs args)
         {
-            foreach(EditorTrack track in TrackSet) {
+            foreach (EditorTrack track in TrackSet) {
                 track.Genre = Genre.Entry.Text;
             }
         }
         
-        private void OnEnterNextTitleClicked(object o, EventArgs args)
+        private void OnEnterNextTitleClicked (object o, EventArgs args)
         {
-            OnNextClicked(o, args);
+            OnNextClicked (o, args);
             Title.HasFocus = true;
-            Title.SelectRegion(0, Title.Text.Length);
+            Title.SelectRegion (0, Title.Text.Length);
         }
         
-        private void OnTitleActivated(object o, EventArgs args)
+        private void OnTitleActivated (object o, EventArgs args)
         {
-            if(EnterNextTitle.Sensitive) {
-                OnEnterNextTitleClicked(o, args);
+            if (EnterNextTitle.Sensitive) {
+                OnEnterNextTitleClicked (o, args);
             }
         }
         
         private string last_path = null;
         
-        private void OnCoverButtonClicked(object o, EventArgs args)
+        private void OnCoverButtonClicked (object o, EventArgs args)
         {
-            Banshee.Gui.Dialogs.ImageFileChooserDialog chooser = new Banshee.Gui.Dialogs.ImageFileChooserDialog();
+            Banshee.Gui.Dialogs.ImageFileChooserDialog chooser = new Banshee.Gui.Dialogs.ImageFileChooserDialog ();
             chooser.LocalOnly = true;
             
             try {
                 string path = (TrackSet[currentIndex] as EditorTrack).CoverArtFilename;
-                path = System.IO.Path.GetDirectoryName(path);
+                path = System.IO.Path.GetDirectoryName (path);
                 
-                if(path != null && path != String.Empty) {
-                    chooser.SetCurrentFolder(path);
-                } else if(last_path != null && last_path != String.Empty) {
-                    path = System.IO.Path.GetDirectoryName(last_path);
-                    chooser.SetCurrentFolder(path);
+                if (path != null && path != String.Empty) {
+                    chooser.SetCurrentFolder (path);
+                } else if (last_path != null && last_path != String.Empty) {
+                    path = System.IO.Path.GetDirectoryName (last_path);
+                    chooser.SetCurrentFolder (path);
                 }
             } catch {
             }
             
-            if(chooser.Run() == (int)ResponseType.Ok) {
+            if (chooser.Run () == (int)ResponseType.Ok) {
                 last_path = chooser.Filename;
                 
                 try {
-                    Gdk.Pixbuf pixbuf = new Gdk.Pixbuf(chooser.Filename).ScaleSimple(100, 100, Gdk.InterpType.Bilinear);
-                    SetCoverImage(pixbuf);
+                    Gdk.Pixbuf pixbuf = new Gdk.Pixbuf (chooser.Filename).ScaleSimple (100, 100, Gdk.InterpType.Bilinear);
+                    SetCoverImage (pixbuf);
                     (TrackSet[currentIndex] as EditorTrack).CoverArtFilename = chooser.Filename;
                     (TrackSet[currentIndex] as EditorTrack).CoverArtThumbnail = pixbuf;
                 } catch {
-                    SetCoverImage(null);
+                    SetCoverImage (null);
                 }
             }
             
-            chooser.Destroy();
+            chooser.Destroy ();
         }
         
-        private EditorTrack UpdateCurrent()
+        private EditorTrack UpdateCurrent ()
         {
-            if(currentIndex < 0 || currentIndex >= TrackSet.Count) {
+            if (currentIndex < 0 || currentIndex >= TrackSet.Count) {
                 return null;
             }
                 
@@ -573,7 +574,7 @@
             track.Rating = rating_entry.Value;
             
             try {
-                track.Year = Convert.ToInt32(Year.Text);
+                track.Year = Convert.ToInt32 (Year.Text);
             } catch {
                 track.Year = 0;
             }
@@ -581,42 +582,58 @@
             return track;
         }
 
-        private void OnCancelButtonClicked(object o, EventArgs args)
+        private void OnCancelButtonClicked (object o, EventArgs args)
         {
-            Window.Destroy();
+            Window.Destroy ();
         }
         
-        private void OnSaveButtonClicked(object o, EventArgs args)
+        private List<int> primary_sources = new List<int> ();
+        private void OnSaveButtonClicked (object o, EventArgs args)
         {
-            UpdateCurrent();
+            UpdateCurrent ();
             
-            // TODO improve performance by not notifying each track's source until
-            // we've saved all tracks.  Right now, if you edit two tracks in your library, the
-            // Library is reloaded twice.
-            foreach(EditorTrack track in TrackSet) {
-                SaveTrack(track, true);
-            }
-            
-            EventHandler handler = Saved;
-            if(handler != null) {
-                handler(this, new EventArgs());
+            // TODO wrap in db transaction
+            try {
+                DatabaseTrackInfo.NotifySaved = false;
+
+                primary_sources.Clear ();
+                foreach (EditorTrack track in TrackSet) {
+                    SaveTrack (track);
+
+                    if (track.Track is DatabaseTrackInfo) {
+                        int id = (track.Track as DatabaseTrackInfo).PrimarySourceId;
+                        if (!primary_sources.Contains (id)) {
+                            primary_sources.Add (id);
+                        }
+                    }
+                }
+                
+                EventHandler handler = Saved;
+                if (handler != null) {
+                    handler (this, new EventArgs ());
+                }
+
+                // Finally, notify the affected primary sources
+                foreach (int id in primary_sources) {
+                    PrimarySource.GetById (id).NotifyTracksChanged ();
+                }
+            } finally {
+                DatabaseTrackInfo.NotifySaved = true;
             }
             
-            Window.Destroy();
+            Window.Destroy ();
         }
         
-        private void SaveTrack (EditorTrack track, bool writeToDatabase)
+        private void SaveTrack (EditorTrack track)
         {
             track.Save ();
             
-            if (writeToDatabase) {
-                track.Track.Save ();
+            track.Track.Save ();
                 
-                if (LibrarySchema.WriteMetadata.Get ()) {
-                    SaveToFile (track);
-                }
+            if (LibrarySchema.WriteMetadata.Get ()) {
+                SaveToFile (track);
             }
-                
+
             if (track.Track == ServiceManager.PlayerEngine.CurrentTrack) {
                 ServiceManager.PlayerEngine.TrackInfoUpdated ();
             }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/ConnectedSeekSlider.cs	Fri May  9 18:40:15 2008
@@ -61,6 +61,8 @@
                 PlayerEvent.Buffering |
                 PlayerEvent.StartOfStream |
                 PlayerEvent.StateChange);
+                
+            ServiceManager.PlayerEngine.TrackIntercept += OnTrackIntercept;
             
             seek_slider.SeekRequested += OnSeekRequested;
         }
@@ -98,6 +100,13 @@
             Add (box);
         }
         
+        private bool transitioning = false;
+        private bool OnTrackIntercept (Banshee.Collection.TrackInfo track)
+        {
+            transitioning = true;
+            return false;
+        }
+        
         private void OnPlayerEvent (PlayerEventArgs args)
         {
             switch (args.Event) {
@@ -125,12 +134,14 @@
                             stream_position_label.IsContacting = true;
                             seek_slider.SetIdle ();
                             break;
-                        case PlayerState.Loaded:
-                            seek_slider.Duration = ServiceManager.PlayerEngine.CurrentTrack.Duration.TotalSeconds;
-                            break;
                         case PlayerState.Idle:
-                            seek_slider.SetIdle ();
-                            stream_position_label.IsContacting = false;
+                            if (!transitioning) {
+                                seek_slider.SetIdle ();
+                                stream_position_label.IsContacting = false;
+                            }
+                            break;
+                        default:
+                            transitioning = false;
                             break;
                     }
                     break;
@@ -145,7 +156,6 @@
             
             uint stream_length = ServiceManager.PlayerEngine.Length;
             uint stream_position = ServiceManager.PlayerEngine.Position;
-            
             stream_position_label.IsContacting = false;
             seek_slider.CanSeek = ServiceManager.PlayerEngine.CanSeek;
             seek_slider.Duration = stream_length;

Modified: trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/StreamPositionLabel.cs	Fri May  9 18:40:15 2008
@@ -47,7 +47,6 @@
             
             this.seekRange = seekRange;
             this.seekRange.ValueChanged += OnSliderUpdated;
-            this.seekRange.DurationChanged += OnSliderUpdated;
         }
         
         protected override void OnRealized ()
@@ -119,15 +118,18 @@
             return true;
         }
         
+        private static string idle = Catalog.GetString ("Idle");
+        private static string contacting = Catalog.GetString ("Contacting...");
+
         private void UpdateLabel ()
         {
             if (is_buffering) {
                 double progress = buffering_progress * 100.0;
-                UpdateLabel (Catalog.GetString("Buffering") + ": " + progress.ToString ("0.0") + "%");
+                UpdateLabel (String.Format ("{0}: {1}%", Catalog.GetString("Buffering"), progress.ToString ("0.0")));
             } else if (is_contacting) {
-                UpdateLabel (Catalog.GetString ("Contacting..."));
+                UpdateLabel (contacting);
             } else if (seekRange.Value == 0 && seekRange.Duration == 0) {
-                UpdateLabel (Catalog.GetString ("Idle"));
+                UpdateLabel (idle);
             } else if (seekRange.Value == seekRange.Duration) {
                 UpdateLabel (FormatDuration ((long)seekRange.Value));
             } else {

Modified: trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs	Fri May  9 18:40:15 2008
@@ -162,7 +162,7 @@
             );
 
             if (count > 0) {
-                SetStatus (Catalog.GetString ("Auto rip off because this album is already in the Music Library."), true, false, null);
+                SetStatus (Catalog.GetString ("Automatic import off since this album is already in the Music Library."), true, false, null);
                 return;
             }
 

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs	Fri May  9 18:40:15 2008
@@ -290,6 +290,11 @@
             }
         }
 
+        public void ClearManagedCache ()
+        {
+            base.Clear ();
+        }
+
         private bool saved_selection = false;
         public void SaveSelection ()
         {

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryNode.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryNode.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryNode.cs	Fri May  9 18:40:15 2008
@@ -173,6 +173,32 @@
             } while (queue.Count > 0);
         }
 
+        public IEnumerable<QueryField> GetFields ()
+        {
+            foreach (QueryTermNode term in GetTerms ())
+                yield return term.Field;
+        }
+
+        public IEnumerable<QueryTermNode> GetTerms ()
+        {
+            Queue<QueryNode> queue = new Queue<QueryNode> ();
+            queue.Enqueue (this);
+            do {
+                QueryNode node = queue.Dequeue ();
+                QueryListNode list = node as QueryListNode;
+                if (list != null) {
+                    foreach (QueryNode child in list.Children) {
+                        queue.Enqueue (child);
+                    }
+                } else {
+                    QueryTermNode term = node as QueryTermNode;
+                    if (term != null) {
+                        yield return term;
+                    }
+                }
+            } while (queue.Count > 0);
+        }
+
         public override string ToString ()
         {
             return ToUserQuery ();

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOrder.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOrder.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/QueryOrder.cs	Fri May  9 18:40:15 2008
@@ -51,7 +51,12 @@
             get { return order_sql; }
         }
 
-        public QueryOrder (string name, string label, string order_sql)
+        private QueryField field;
+        public QueryField Field {
+            get { return field; }
+        }
+
+        public QueryOrder (string name, string label, string order_sql, QueryField field)
         {
             this.name = name;
             this.label = label;



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