banshee r3378 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Database src/Core/Banshee.Services/Banshee.Library src/Core/Banshee.Services/Banshee.Playlist src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.Services/Banshee.Sources src/Core/Banshee.ThickClient/Banshee.Sources.Gui src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue src/Libraries/Hyena/Hyena.Data.Sqlite



Author: gburt
Date: Tue Mar  4 22:33:12 2008
New Revision: 3378
URL: http://svn.gnome.org/viewvc/banshee?rev=3378&view=rev

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

	This commit adds more granular events for when tracks are added, changed,
	and removed to each track's PrimarySource.  Playlists etc can listen for
	these events, and only reload when necessary (still work to do on this
	front).  Reloads happen not in the main thread, so actions (like rating
	4800 songs at once) return quickly, but other sources may take a second or
	few to be updated.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs:
	In Save method, trigger PrimarySource's TrackAdded or Changed method as
	appropriate.

	* src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs:
	Cache the unfiltered query string.

	* src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs: Call
	OnImportFinished even if cancelled.

	* src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs:
	Add CoreRemovedTracks table.

	* src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs: Pass
	false to DatabaseTrackInfo.Save method, and manually trigger TracksAdded
	event every 500 songs or when finished/cancelled.

	* src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs: Use
	RemoveTrackRange instead of remove_track_command.

	* src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs:
	Add stamps for tracking when last handled Tracks* events, add count
	commands for use in determining if an event affects this source.

	* src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs:
	* src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs: Handle
	TracksChanged and TracksRemoved events from our PrimarySource parent.
	Right now we just always trigger a Reload, but these handlers are ripe for
	optimizations that will avoid Reloads unless absolutely necessary.

	* src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs: Trigger the
	Track* events on the primary source when adding/changing/removing tracks.

	* src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs: Add
	TracksAdded, TracksChanged, and TracksRemoved methods.

	* src/Core/Banshee.Services/Banshee.Sources/Source.cs: Make
	SetParentSource a virtual method.

	* src/Core/Banshee.Services/Banshee.Collection/BansheeListModel.cs:
	* src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs:
	* src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView.cs: Make
	sure GUI-related events are handled in the main loop.

	* src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs:

	* src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs: Turn
	DateTime values into integers.

	* src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs: Add
	another override for Query/Query<T>/Execute (HyenaSqliteCommand, params).

	* src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs: Cache the
	reload command.


Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/BansheeListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibrarySource.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.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.Services/Banshee.Sources/Source.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView.cs
   trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs

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	Tue Mar  4 22:33:12 2008
@@ -77,9 +77,14 @@
         public void Save (bool notify)
         {
             DateUpdated = DateTime.Now;
+            bool is_new = dbid == 0;
             Provider.Save (this);
             if (notify) {
-                Source.OnTracksUpdated ();
+                if (is_new) {
+                    Source.NotifyTracksAdded ();
+                } else {
+                    Source.NotifyTracksChanged ();
+                }
             }
         }
         

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	Tue Mar  4 22:33:12 2008
@@ -50,11 +50,11 @@
         private readonly BansheeDbConnection connection;
         private readonly BansheeModelProvider<DatabaseTrackInfo> provider;
         private BansheeModelCache<DatabaseTrackInfo> cache;
-        private int count;
+        private long count;
         private TimeSpan duration;
         private long filesize;
 
-        private int filtered_count;
+        private long filtered_count;
         private TimeSpan filtered_duration;
         private long filtered_filesize;
         
@@ -172,17 +172,27 @@
             filtered_count = 0;
             OnCleared();
         }
-        
-        private bool first_reload = true;
-        public override void Reload()
+
+        private string unfiltered_query;
+        protected string UnfilteredQuery {
+            get {
+                return unfiltered_query ?? unfiltered_query = String.Format (
+                    "FROM {0}{1} WHERE {2} {3}",
+                    provider.From, JoinFragment, provider.Where, ConditionFragment
+                );
+            }
+        }
+
+        /*public void UpdateAggregates ()
         {
-            string unfiltered_query = String.Format (
-                "FROM {0}{1} WHERE {2} {3}",
-                provider.From, JoinFragment, provider.Where, ConditionFragment
-            );
+            UpdateUnfilteredAggregates ();
+            UpdateFilteredAggregates ();
+        }*/
 
+        private void UpdateUnfilteredAggregates ()
+        {
             using (IDataReader reader = connection.Query (String.Format (
-                "SELECT COUNT(*), {0} {1}", SelectAggregates, unfiltered_query)))
+                "SELECT COUNT(*), {0} {1}", SelectAggregates, UnfilteredQuery)))
             {
                 if (reader.Read ()) {
                     count = Convert.ToInt32 (reader[0]);
@@ -190,10 +200,21 @@
                     filesize = reader.IsDBNull (2) ? 0 : Convert.ToInt64 (reader[2]);
                 }
             }
+        }
+
+        /*private void UpdateFilteredAggregates ()
+        {
+            cache.UpdateAggregates ();
+            filtered_count = cache.Count;
+        }*/
+        
+        private bool first_reload = true;
+        public override void Reload()
+        {
+            UpdateUnfilteredAggregates ();
 
             StringBuilder qb = new StringBuilder ();
-                
-            qb.Append (unfiltered_query);
+            qb.Append (UnfilteredQuery);
             
             if (artist_id_filter_query != null) {
                 qb.Append ("AND ");
@@ -230,7 +251,7 @@
         public override int IndexOf (TrackInfo track)
         {
             DatabaseTrackInfo library_track = track as DatabaseTrackInfo;
-            return library_track == null ? -1 : cache.IndexOf ((int)library_track.DbId);
+            return (int) (library_track == null ? -1 : cache.IndexOf ((int)library_track.DbId));
         }
 
         public override TrackInfo this[int index] {
@@ -238,7 +259,7 @@
         }
 
         public override int Count {
-            get { return filtered_count; }
+            get { return (int) filtered_count; }
         }
 
         public TimeSpan Duration {
@@ -250,7 +271,7 @@
         }
         
         public int UnfilteredCount {
-            get { return count; }
+            get { return (int) count; }
         }
 
         public TimeSpan FilteredDuration {
@@ -362,7 +383,7 @@
         }
 
         public int CacheId {
-            get { return cache.CacheId; }
+            get { return (int) cache.CacheId; }
         }
 
         public ISortableColumn SortColumn { 
@@ -384,6 +405,25 @@
             return this[index].GenerateExportable();
         }
 
+        private string track_ids_sql;
+        public string TrackIdsSql {
+            get {
+                if (track_ids_sql == null) {
+                    if (JoinTable == null) {
+                        track_ids_sql = "SELECT ItemID FROM CoreCache WHERE ModelID = ? LIMIT ?, ?";
+                    } else {
+                        track_ids_sql = String.Format (
+                            "SELECT {0} FROM {1} WHERE {2} IN (SELECT ItemID FROM CoreCache WHERE ModelID = ? LIMIT ?, ?)",
+                            JoinColumn, JoinTable, JoinPrimaryKey
+                        );
+                    }
+                }
+                return track_ids_sql;
+            }
+        }
+
+
+
         // Implement ICacheableModel
         public int FetchCount {
             get { return RowsInView > 0 ? RowsInView * 5 : 100; }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/BansheeListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/BansheeListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/BansheeListModel.cs	Tue Mar  4 22:33:12 2008
@@ -52,20 +52,24 @@
         
         protected virtual void OnCleared ()
         {
-            EventHandler handler = Cleared;
-            if(handler != null) {
-                handler(this, EventArgs.Empty);
-            }
+            Banshee.Base.ThreadAssist.ProxyToMain (delegate {
+                EventHandler handler = Cleared;
+                if(handler != null) {
+                    handler(this, EventArgs.Empty);
+                }
+            });
         }
         
         protected virtual void OnReloaded ()
         {
             selection.Clear ();
 
-            EventHandler handler = Reloaded;
-            if(handler != null) {
-                handler(this, EventArgs.Empty);
-            }
+            Banshee.Base.ThreadAssist.ProxyToMain (delegate {
+                EventHandler handler = Reloaded;
+                if(handler != null) {
+                    handler(this, EventArgs.Empty);
+                }
+            });
         }
         
         public virtual void Clear()

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs	Tue Mar  4 22:33:12 2008
@@ -131,6 +131,7 @@
             path_queue.Clear ();
             processing_queue = false;
             DestroyUserJob ();
+            OnImportFinished ();
         }
         
         private void Enqueue (string path)

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	Tue Mar  4 22:33:12 2008
@@ -242,6 +242,7 @@
             Execute("DROP TABLE IF EXISTS CorePlaylistEntries");
             Execute("DROP TABLE IF EXISTS CoreSmartPlaylists");
             Execute("DROP TABLE IF EXISTS CoreSmartPlaylistEntries");
+            Execute("DROP TABLE IF EXISTS CoreRemovedTracks");
             Execute("DROP TABLE IF EXISTS CoreTracksCache");
             Execute("DROP TABLE IF EXISTS CoreCache");
             
@@ -387,6 +388,14 @@
             Execute("CREATE INDEX CoreSmartPlaylistEntriesTrackIndex ON CoreSmartPlaylistEntries(SmartPlaylistID, TrackID)");
 
             Execute(@"
+                CREATE TABLE CoreRemovedTracks (
+                    TrackID             INTEGER NOT NULL,
+                    Uri                 TEXT,
+                    DateRemovedStamp    INTEGER
+                )
+            ");
+
+            Execute(@"
                 CREATE TABLE CoreCacheModels (
                     CacheID             INTEGER PRIMARY KEY,
                     ModelID             TEXT
@@ -554,7 +563,7 @@
                     job.Progress = (double)++count / (double)total;
                 }
             }
-            ServiceManager.SourceManager.Library.OnTracksUpdated ();
+            ServiceManager.SourceManager.Library.NotifyTracksChanged ();
 
             job.Finish ();
         }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs	Tue Mar  4 22:33:12 2008
@@ -95,8 +95,6 @@
                 return;
             }
 
-            ServiceManager.SourceManager.Library.Importing = true;
-            
             try {            
                 DatabaseTrackInfo track = AddTrackToLibrary (path);
                 if (track != null && track.DbId > 0) {
@@ -114,6 +112,7 @@
             return AddTrackToLibrary (new SafeUri (path));
         }
         
+        private int count = 0;
         public DatabaseTrackInfo AddTrackToLibrary (SafeUri uri)
         {
             DatabaseTrackInfo track = null;
@@ -145,13 +144,17 @@
 
                 album.Save ();
                 artist.Save ();
-                track.Save ();
+                track.Save (false);
 
                 ServiceManager.DbConnection.CommitTransaction ();
             } catch (Exception) {
                 ServiceManager.DbConnection.RollbackTransaction ();
                 throw;
             }
+
+            if (count++ % 500 == 0) {
+                ServiceManager.SourceManager.Library.NotifyTracksAdded ();
+            }
             
             return track;
         }
@@ -175,7 +178,7 @@
 
         protected override void OnImportFinished ()
         {
-            ServiceManager.SourceManager.Library.Importing = false;
+            ServiceManager.SourceManager.Library.NotifyTracksAdded ();
             base.OnImportFinished ();
         }
         

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs	Tue Mar  4 22:33:12 2008
@@ -30,6 +30,8 @@
 using System.Collections.Generic;
 
 using Mono.Unix;
+
+using Hyena;
 using Hyena.Collections;
 using Hyena.Data.Sqlite;
 
@@ -67,13 +69,12 @@
                     } catch (System.IO.FileNotFoundException) {
                     } catch (System.IO.DirectoryNotFoundException) {
                     }
-
-                    // Remove from database
-                    remove_track_command.ApplyValues (track.DbId, track.DbId, track.DbId);
-                    ServiceManager.DbConnection.Execute (remove_track_command);
                 } catch (Exception e) {
                     ErrorSource.AddMessage (e.Message, track.Uri.ToString ());
                 }
+
+                // Remove from database
+                RemoveTrackRange (model, range);
             }
         }
 

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	Tue Mar  4 22:33:12 2008
@@ -35,6 +35,7 @@
 using Mono.Unix;
 
 using Hyena.Data;
+using Hyena.Data.Sqlite;
 using Hyena.Collections;
 
 using Banshee.Base;
@@ -54,6 +55,10 @@
         protected abstract string SourcePrimaryKey { get; }
         protected abstract string TrackJoinTable { get; }
 
+        protected DateTime last_added = DateTime.MinValue;
+        protected DateTime last_updated = DateTime.MinValue;
+        protected DateTime last_removed = DateTime.MinValue;
+
         protected virtual string TrackCondition {
             get {
                 return String.Format (
@@ -79,9 +84,24 @@
                 track_model.JoinColumn = "TrackID";
                 track_model.Condition = String.Format (TrackCondition, dbid);
                 AfterInitialized ();
+
+                count_updated_command = new HyenaSqliteCommand (String.Format (
+                    @"SELECT COUNT(*) FROM {0} WHERE {1} = {2} AND TrackID IN (
+                        SELECT TrackID FROM CoreTracks WHERE DateUpdatedStamp > ?)",
+                    TrackJoinTable, SourcePrimaryKey, dbid
+                ));
+
+                count_removed_command = new HyenaSqliteCommand (String.Format (
+                    @"SELECT COUNT(*) FROM {0} WHERE {1} = {2} AND TrackID IN (
+                        SELECT TrackID FROM CoreRemovedTracks WHERE DateRemovedStamp > ?)",
+                    TrackJoinTable, SourcePrimaryKey, dbid
+                ));
             }
         }
 
+        protected HyenaSqliteCommand count_updated_command;
+        protected HyenaSqliteCommand count_removed_command;
+
         public AbstractPlaylistSource (string generic_name, string name) 
             : this (generic_name, name, null, -1, 0)
         {

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	Tue Mar  4 22:33:12 2008
@@ -243,6 +243,7 @@
                 return;
 
             WithTrackSelection (from, AddTrackRange);
+            Reload ();
             OnUserNotifyUpdated ();
         }
 
@@ -268,6 +269,33 @@
             ServiceManager.DbConnection.Execute (remove_track_range_command);
         }
 
+        protected override void HandleTracksChanged (Source sender, TrackEventArgs args)
+        {
+            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]) {
+                    //if (ServiceManager.DbConnection.Query<int> (count_updated_command, last_updated) > 0) {
+                        Reload ();
+                    //}
+                //}
+            }
+        }
+
+        protected override void HandleTracksRemoved (Source sender, TrackEventArgs args)
+        {
+            if (args.When > last_removed) {
+                last_removed = args.When;
+                Reload ();
+                /*if (ServiceManager.DbConnection.Query<int> (count_removed_command, last_removed) > 0) {
+                    //ServiceManager.DbConnection.Execute ("DELETE FROM CoreCache WHERE ModelID = ? AND ItemID IN (SELECT EntryID FROM CorePlaylistEntries WHERE PlaylistID = ? AND TrackID IN (TrackID FROM CoreRemovedTracks))");
+                    ServiceManager.DbConnection.Execute ("DELETE FROM CorePlaylistEntries WHERE TrackID IN (SELECT TrackID FROM CoreRemovedTracks)");
+                    //track_model.UpdateAggregates ();
+                    //OnUpdated ();
+                }*/
+            }
+        }
+
         public static IEnumerable<PlaylistSource> LoadAll ()
         {
             using (IDataReader reader = ServiceManager.DbConnection.Query (
@@ -280,6 +308,17 @@
                 }
             }
         }
+
+        public override void SetParentSource (Source parent)
+        {
+            base.SetParentSource (parent);
+
+            PrimarySource primary = parent as PrimarySource;
+            if (primary != null) {
+                primary.TracksChanged += HandleTracksChanged;
+                primary.TracksRemoved += HandleTracksRemoved;
+            }
+        }
         
         public static int GetPlaylistId (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	Tue Mar  4 22:33:12 2008
@@ -339,6 +339,51 @@
             get { return QueryOrder == BansheeQuery.RandomOrder; }
         }
 
+        protected override void HandleTracksAdded (Source sender, TrackEventArgs args)
+        {
+            if (args.When > last_added) {
+                last_added = args.When;
+                Reload ();
+            }
+        }
+
+        protected override void HandleTracksChanged (Source sender, TrackEventArgs args)
+        {
+            if (args.When > last_updated) {
+                last_updated = args.When;
+                Reload ();
+            }
+        }
+
+        protected override void HandleTracksRemoved (Source sender, TrackEventArgs args)
+        {
+            if (args.When > last_removed) {
+                last_removed = args.When;
+                Reload ();
+                /*if (ServiceManager.DbConnection.Query<int> (count_removed_command, last_removed) > 0) {
+                    if (Limit == null) {
+                        //track_model.UpdateAggregates ();
+                        //OnUpdated ();
+                        Reload ();
+                    } else {
+                        Reload ();
+                    }
+                }*/
+            }
+        }
+
+        public override void SetParentSource (Source parent)
+        {
+            base.SetParentSource (parent);
+
+            PrimarySource primary = parent as PrimarySource;
+            if (primary != null) {
+                primary.TracksAdded += HandleTracksAdded;
+                primary.TracksChanged += HandleTracksChanged;
+                primary.TracksRemoved += HandleTracksRemoved;
+            }
+        }
+
 #endregion
 
         private string PrependCondition (string with)

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	Tue Mar  4 22:33:12 2008
@@ -32,6 +32,7 @@
 
 using Mono.Unix;
 using Hyena.Data;
+using Hyena.Query;
 using Hyena.Data.Sqlite;
 using Hyena.Collections;
 
@@ -40,6 +41,7 @@
 using Banshee.Sources;
 using Banshee.Collection;
 using Banshee.Collection.Database;
+using Banshee.Query;
 
 namespace Banshee.Sources
 {
@@ -136,26 +138,30 @@
         public void Reload (double min_interval_ms)
         {
             ThreadAssist.SpawnFromMain (delegate {
-                reload_limiter.Execute (min_interval_ms);
+                //reload_limiter.Execute (min_interval_ms);
+                RateLimitedReload ();
             });
         }
 
         public void Reload ()
         {
             ThreadAssist.SpawnFromMain (delegate {
-                reload_limiter.Execute (100.0);
+                //reload_limiter.Execute (100.0);
+                RateLimitedReload ();
             });
         }
 
         public virtual void RateLimitedReload ()
         {
-            track_model.Reload ();
-            artist_model.Reload ();
-            album_model.Reload ();
-            OnUpdated ();
+            lock (track_model) {
+                track_model.Reload ();
+                artist_model.Reload ();
+                album_model.Reload ();
+                OnUpdated ();
+            }
         }
 
-        protected virtual void ReloadChildren ()
+        /*protected virtual void ReloadChildren ()
         {
             foreach (Source child in Children) {
                 ITrackModelSource c = child as ITrackModelSource;
@@ -163,7 +169,7 @@
                     c.Reload ();
                 }
             }
-        }
+        }*/
         
         public virtual bool HasDependencies {
             get { return false; }
@@ -173,8 +179,7 @@
         {
             if (index != -1) {
                 RemoveTrackRange (track_model, new RangeCollection.Range (index, index));
-                Reload ();
-                ReloadChildren ();
+                OnTracksRemoved ();
             }
         }
 
@@ -197,6 +202,7 @@
         public virtual void RemoveSelectedTracks (TrackListDatabaseModel model)
         {
             WithTrackSelection (model, RemoveTrackRange);
+            OnTracksRemoved ();
         }
 
         // Methods for deleting tracks from this source
@@ -213,6 +219,7 @@
         public virtual void DeleteSelectedTracks (TrackListDatabaseModel model)
         {
             WithTrackSelection (model, DeleteTrackRange);
+            OnTracksRemoved ();
         }
 
         public virtual void RateSelectedTracks (int rating)
@@ -230,32 +237,64 @@
                 foreach (RangeCollection.Range range in selection.Ranges) {
                     RateTrackRange (model, range, rating);
                 }
-
-                ReloadPrimarySource ();
             }
+            OnTracksChanged (BansheeQuery.RatingField);
         }
 
 #endregion
         
 #region Protected Methods
 
+        protected virtual void OnTracksAdded ()
+        {
+            ThreadAssist.SpawnFromMain (delegate {
+                HandleTracksAdded (this, new TrackEventArgs ());
+                foreach (PrimarySource psource in PrimarySources) {
+                    psource.NotifyTracksAdded ();
+                }
+            });
+        }
+
+        protected void OnTracksChanged ()
+        {
+            OnTracksChanged (null);
+        }
+
+        protected virtual void OnTracksChanged (QueryField field)
+        {
+            ThreadAssist.SpawnFromMain (delegate {
+                HandleTracksChanged (this, new TrackEventArgs (field));
+                foreach (PrimarySource psource in PrimarySources) {
+                    psource.NotifyTracksChanged (field);
+                }
+            });
+        }
+
+        protected virtual void OnTracksRemoved ()
+        {
+            ThreadAssist.SpawnFromMain (delegate {
+                HandleTracksRemoved (this, new TrackEventArgs ());
+                foreach (PrimarySource psource in PrimarySources) {
+                    psource.NotifyTracksRemoved ();
+                }
+            });
+        }
+
         // If we are a PrimarySource, reload ourself and our children, otherwise if our Parent
         // is one, do so for it, otherwise do so for all PrimarySources.
-        protected void ReloadPrimarySource ()
-        {
-            PrimarySource psource;
-            if ((psource = this as PrimarySource) != null) {
-                Reload ();
-                ReloadChildren ();
-            } else {
-                if ((psource = Parent as PrimarySource) != null) {
-                    psource.Reload ();
-                    psource.ReloadChildren ();
+        private IEnumerable<PrimarySource> PrimarySources {
+            get {
+                PrimarySource psource;
+                if ((psource = this as PrimarySource) != null) {
+                    yield return psource;
                 } else {
-                    foreach (Source source in ServiceManager.SourceManager.Sources) {
-                        if ((psource = source as PrimarySource) != null) {
-                            psource.Reload ();
-                            psource.ReloadChildren ();
+                    if ((psource = Parent as PrimarySource) != null) {
+                        yield return psource;
+                    } else {
+                        foreach (Source source in ServiceManager.SourceManager.Sources) {
+                            if ((psource = source as PrimarySource) != null) {
+                                yield return psource;
+                            }
                         }
                     }
                 }
@@ -268,14 +307,14 @@
                 if (rate_track_range_command == null) {
                     if (track_model.JoinTable != null) {
                         rate_track_range_command = new HyenaSqliteCommand (String.Format (@"
-                            UPDATE CoreTracks SET Rating = ? WHERE
+                            UPDATE CoreTracks SET Rating = ?, DateUpdatedStamp = ? WHERE
                                 TrackID IN (SELECT TrackID FROM {0} WHERE 
                                     {1} IN (SELECT ItemID FROM CoreCache WHERE ModelID = {2} LIMIT ?, ?))",
                             track_model.JoinTable, track_model.JoinPrimaryKey, track_model.CacheId
                         ));
                     } else {
                         rate_track_range_command = new HyenaSqliteCommand (String.Format (@"
-                            UPDATE CoreTracks SET Rating = ? WHERE TrackID IN (
+                            UPDATE CoreTracks SET Rating = ?, DateUpdatedStamp = ? WHERE TrackID IN (
                                 SELECT ItemID FROM CoreCache WHERE ModelID = {0} LIMIT ?, ?)",
                             track_model.CacheId
                         ));
@@ -305,7 +344,7 @@
 
         protected virtual void RateTrackRange (TrackListDatabaseModel model, RangeCollection.Range range, int rating)
         {
-            RateTrackRangeCommand.ApplyValues (rating, range.Start, range.End - range.Start + 1);
+            RateTrackRangeCommand.ApplyValues (rating, DateTime.Now, range.Start, range.End - range.Start + 1);
             ServiceManager.DbConnection.Execute (RateTrackRangeCommand);
         }
 
@@ -319,11 +358,21 @@
                 foreach (RangeCollection.Range range in selection.Ranges) {
                     handler (model, range);
                 }
-                Reload ();
-                ReloadChildren ();
             }
         }
 
+        protected virtual void HandleTracksAdded (Source sender, TrackEventArgs args)
+        {
+        }
+
+        protected virtual void HandleTracksChanged (Source sender, TrackEventArgs args)
+        {
+        }
+
+        protected virtual void HandleTracksRemoved (Source sender, TrackEventArgs args)
+        {
+        }
+
 #endregion
 
     }

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	Tue Mar  4 22:33:12 2008
@@ -31,6 +31,7 @@
 
 using Mono.Unix;
 using Hyena.Data;
+using Hyena.Query;
 using Hyena.Data.Sqlite;
 using Hyena.Collections;
 
@@ -39,33 +40,43 @@
 using Banshee.Sources;
 using Banshee.Collection;
 using Banshee.Collection.Database;
+using Banshee.Query;
 
 namespace Banshee.Sources
 {
+    public class TrackEventArgs : EventArgs
+    {
+        private DateTime when;
+        public DateTime When {
+            get { return when; }
+        }
+
+        private QueryField changed_field;
+        public QueryField ChangedField {
+            get { return changed_field; }
+        }
+
+        public TrackEventArgs ()
+        {
+            when = DateTime.Now;
+        }
+
+        public TrackEventArgs (QueryField field) : this ()
+        {
+            this.changed_field = field;
+        }
+    }
+
     public abstract class PrimarySource : DatabaseSource
     {
         protected ErrorSource error_source = new ErrorSource (Catalog.GetString ("Import Errors"));
         protected bool error_source_visible = false;
-        protected RateLimiter tracks_updated_limiter;
+        //protected RateLimiter tracks_updated_limiter;
         private double tracks_updated_ms = 250.0;
 
-        protected HyenaSqliteCommand remove_range_command = new HyenaSqliteCommand (@"
-            DELETE FROM CoreTracks WHERE TrackID IN
-                (SELECT ItemID FROM CoreCache
-                    WHERE ModelID = ? LIMIT ?, ?);
-            DELETE FROM CorePlaylistEntries WHERE TrackID IN
-                (SELECT ItemID FROM CoreCache
-                    WHERE ModelID = ? LIMIT ?, ?);
-            DELETE FROM CoreSmartPlaylistEntries WHERE TrackID IN
-                (SELECT ItemID FROM CoreCache
-                    WHERE ModelID = ? LIMIT ?, ?)"
-        );
-
-        protected HyenaSqliteCommand remove_track_command = new HyenaSqliteCommand (@"
-            DELETE FROM CoreTracks WHERE TrackID = ?;
-            DELETE FROM CorePlaylistEntries WHERE TrackID = ?;
-            DELETE FROM CoreSmartPlaylistEntries WHERE TrackID = ?"
-        );
+        protected string remove_range_sql = @"
+            INSERT INTO CoreRemovedTracks SELECT ?, TrackID, Uri FROM CoreTracks WHERE TrackID IN ({0});
+            DELETE FROM CoreTracks WHERE TrackID IN ({0})";
 
         protected int source_id;
         public int SourceId {
@@ -76,7 +87,11 @@
             get { return error_source; }
         }
 
-        public event EventHandler TracksUpdated;
+        public delegate void TrackEventHandler (Source sender, TrackEventArgs args);
+
+        public event TrackEventHandler TracksAdded;
+        public event TrackEventHandler TracksChanged;
+        public event TrackEventHandler TracksRemoved;
 
         private static Dictionary<int, PrimarySource> primary_sources = new Dictionary<int, PrimarySource> ();
         public static PrimarySource GetById (int id)
@@ -95,7 +110,7 @@
             error_source.Updated += OnErrorSourceUpdated;
             OnErrorSourceUpdated (null, null);
 
-            tracks_updated_limiter = new RateLimiter (20.0, tracks_updated_ms, RateLimitedOnTracksUpdated);
+            //tracks_updated_limiter = new RateLimiter (20.0, tracks_updated_ms, RateLimitedOnTracksUpdated);
 
             primary_sources[source_id] = this;
         }
@@ -104,26 +119,24 @@
             set { tracks_updated_ms = value ? 5000.0 : 250.0; }
         }
 
-        public void OnTracksUpdated ()
+        internal void NotifyTracksAdded ()
         {
-            ThreadAssist.Spawn (delegate {
-                tracks_updated_limiter.Execute (tracks_updated_ms);
-            });
+            OnTracksAdded ();
         }
 
-        protected virtual void RateLimitedOnTracksUpdated ()
+        internal void NotifyTracksChanged (QueryField field)
         {
-            RateLimitedReload ();
-            foreach (Source child in Children) {
-                if (child is DatabaseSource) {
-                    (child as DatabaseSource).RateLimitedReload ();
-                }
-            }
+            OnTracksChanged (field);
+        }
 
-            EventHandler handler = TracksUpdated;
-            if (handler != null) {
-                handler (this, EventArgs.Empty);
-            }
+        internal void NotifyTracksChanged ()
+        {
+            OnTracksChanged ();
+        }
+
+        internal void NotifyTracksRemoved ()
+        {
+            OnTracksRemoved ();
         }
 
         protected void OnErrorSourceUpdated (object o, EventArgs args)
@@ -163,14 +176,59 @@
             Reload ();
         }*/
 
+        public override void SetParentSource (Source source)
+        {
+            if (source is PrimarySource) {
+                throw new ArgumentException ("PrimarySource cannot have another PrimarySource as its parent");
+            }
+
+            base.SetParentSource (source);
+        }
+
+        protected override void OnTracksAdded ()
+        {
+            ThreadAssist.SpawnFromMain (delegate {
+                Reload ();
+
+                TrackEventHandler handler = TracksAdded;
+                if (handler != null) {
+                    handler (this, new TrackEventArgs ());
+                }
+            });
+        }
+
+        protected override void OnTracksChanged (QueryField field)
+        {
+            ThreadAssist.SpawnFromMain (delegate {
+                Reload ();
+
+                TrackEventHandler handler = TracksChanged;
+                if (handler != null) {
+                    handler (this, new TrackEventArgs (field));
+                }
+            });
+        }
+
+        protected override void OnTracksRemoved ()
+        {
+            ThreadAssist.SpawnFromMain (delegate {
+                Reload ();
+
+                TrackEventHandler handler = TracksRemoved;
+                if (handler != null) {
+                    handler (this, new TrackEventArgs ());
+                }
+            });
+        }
+
         protected override void RemoveTrackRange (TrackListDatabaseModel model, RangeCollection.Range range)
         {
-            remove_range_command.ApplyValues (
-                    model.CacheId, range.Start, range.End - range.Start + 1,
-                    model.CacheId, range.Start, range.End - range.Start + 1,
-                    model.CacheId, range.Start, range.End - range.Start + 1
+            ServiceManager.DbConnection.Execute (
+                String.Format (remove_range_sql, model.TrackIdsSql),
+                DateTime.Now,
+                model.CacheId, range.Start, range.End - range.Start + 1,
+                model.CacheId, range.Start, range.End - range.Start + 1
             );
-            ServiceManager.DbConnection.Execute (remove_range_command);
         }
     }
 }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/Source.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/Source.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/Source.cs	Tue Mar  4 22:33:12 2008
@@ -128,7 +128,7 @@
             get { return SourceMergeType.None; }
         }
         
-        public void SetParentSource (Source parent)
+        public virtual void SetParentSource (Source parent)
         {
             this.parent = parent;
         }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs	Tue Mar  4 22:33:12 2008
@@ -176,12 +176,14 @@
         
         private void OnSourceUpdated(object o, EventArgs args)
         {
-            SourceEventHandler handler = SourceUpdated;
-            if(handler != null) {
-                SourceEventArgs evargs = new SourceEventArgs();
-                evargs.Source = o as Source;
-                handler(evargs);
-            }
+            Banshee.Base.ThreadAssist.ProxyToMain (delegate {
+                SourceEventHandler handler = SourceUpdated;
+                if(handler != null) {
+                    SourceEventArgs evargs = new SourceEventArgs();
+                    evargs.Source = o as Source;
+                    handler(evargs);
+                }
+            });
         }
 
         private void OnChildSourceAdded(SourceEventArgs args)

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView.cs	Tue Mar  4 22:33:12 2008
@@ -112,9 +112,11 @@
             };
             
             ServiceManager.SourceManager.SourceUpdated += delegate (SourceEventArgs args) {
-                TreeIter iter = FindSource (args.Source);
-                store.SetValue (iter, 1, args.Source.Order);
-                QueueDraw ();
+                Banshee.Base.ThreadAssist.ProxyToMain (delegate {
+                    TreeIter iter = FindSource (args.Source);
+                    store.SetValue (iter, 1, args.Source.Order);
+                    QueueDraw ();
+                });
             };
             
             ServiceManager.PlaybackController.SourceChanged += delegate {
@@ -417,12 +419,14 @@
 
         private void OnSourceUserNotifyUpdated (object o, EventArgs args)
         {
-            TreeIter iter = FindSource ((Source)o);
-            if (iter.Equals (TreeIter.Zero)) {
-                return;
-            }
-            
-            notify_stage.AddOrReset (iter);
+            Banshee.Base.ThreadAssist.ProxyToMain (delegate {
+                TreeIter iter = FindSource ((Source)o);
+                if (iter.Equals (TreeIter.Zero)) {
+                    return;
+                }
+                
+                notify_stage.AddOrReset (iter);
+            });
         }
         
 #endregion

Modified: trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	Tue Mar  4 22:33:12 2008
@@ -102,6 +102,9 @@
             
             UpdateActions ();
             ServiceManager.SourceManager.ActiveSourceChanged += delegate { UpdateActions (); };
+
+            ServiceManager.SourceManager.Library.TracksChanged += HandleTracksChanged;
+            ServiceManager.SourceManager.Library.TracksRemoved += HandleTracksRemoved;
         }
         
         public void Dispose ()

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs	Tue Mar  4 22:33:12 2008
@@ -149,6 +149,8 @@
             for (int i = 0; i < parameter_count; i++) {
                 if (param_values[i] is string) {
                     param_values[i] = String.Format ("'{0}'", (param_values[i] as string).Replace ("'", "''"));
+                } else if (param_values[i] is DateTime) {
+                    param_values[i] = DateTimeUtil.FromDateTime ((DateTime) param_values[i]);
                 } else if (param_values[i] == null) {
                     param_values[i] = "NULL";
                 }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs	Tue Mar  4 22:33:12 2008
@@ -84,6 +84,11 @@
             }
         }
 
+        public IDataReader Query (HyenaSqliteCommand command, params object [] param_values)
+        {
+            return Query (command.ApplyValues (param_values));
+        }
+
         public IDataReader Query (string command_str, params object [] param_values)
         {
             return Query (new HyenaSqliteCommand (command_str, param_values));
@@ -109,6 +114,11 @@
                 : (T) SqliteUtils.FromDbFormat (typeof (T), Convert.ChangeType (result, typeof (T)));
         }
 
+        public T Query<T> (HyenaSqliteCommand command, params object [] param_values)
+        {
+            return Query<T> (command.ApplyValues (param_values));
+        }
+
         public T Query<T> (string command_str, params object [] param_values)
         {
             return Query<T> (new HyenaSqliteCommand (command_str, param_values));
@@ -129,6 +139,11 @@
             }
         }
 
+        public int Execute (HyenaSqliteCommand command, params object [] param_values)
+        {
+            return Execute (command.ApplyValues (param_values));
+        }
+
         public int Execute (string command_str, params object [] param_values)
         {
             return Execute (new HyenaSqliteCommand (command_str, param_values));

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	Tue Mar  4 22:33:12 2008
@@ -206,11 +206,22 @@
             return target_id - first_order_id;
         }
 
+        private HyenaSqliteCommand last_reload_command;
+        private string last_reload_fragment;
+
         public override int Reload ()
         {
+            if (last_reload_fragment == model.ReloadFragment) {
+                last_reload_command = last_reload_command;
+            } else {
+                last_reload_fragment = model.ReloadFragment;
+                last_reload_command = new HyenaSqliteCommand (reload_sql + last_reload_fragment);
+            }
+
             Clear ();
+
             //using (new Timer (String.Format ("Generating cache table for {0}", model))) {
-                connection.Execute (reload_sql + model.ReloadFragment);
+                connection.Execute (last_reload_command);
             //}
             first_order_id = -1;
             UpdateAggregates ();



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