Index: src/PlayerInterface.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/PlayerInterface.cs,v
retrieving revision 1.228
diff -u -r1.228 PlayerInterface.cs
--- src/PlayerInterface.cs	9 Nov 2006 00:50:53 -0000	1.228
+++ src/PlayerInterface.cs	27 Nov 2006 07:47:32 -0000
@@ -1037,19 +1037,63 @@
         private void OnSourceTrackAdded(object o, TrackEventArgs args)
         {
             if(SourceManager.ActiveSource == o) {
-                if(searchEntry.IsQueryAvailable && !DoesTrackMatchSearch(args.Track)) {
+                // We only want to update the playlist view once
+                if (args.Track != null) {
+                    AddTrackToModel(args.Track, (args.Tracks == null || args.Tracks.Count == 0));
+                }
+
+                if (args.Tracks != null) {
+                    if (args.Tracks.Count > 50)
+                        playlistModel.ClearSortOrder();
+
+                    int i = 1, last = args.Tracks.Count;
+                    foreach (TrackInfo track in args.Tracks) {
+                        AddTrackToModel(track, i++ == last);
+                    }
+
+                    if (args.Tracks.Count > 50)
+                        playlistModel.RestoreSortOrder();
+                }
+            }
+        }
+
+        private void AddTrackToModel(TrackInfo track, bool update)
+        {
+            ThreadAssist.ProxyToMain(delegate {
+                if(searchEntry.IsQueryAvailable && !DoesTrackMatchSearch(track)) {
                     return;
                 }
                 
-                playlistModel.AddTrack(args.Track);
-            }
+                playlistModel.AddTrack(track, update);
+            });
         }
         
         private void OnSourceTrackRemoved(object o, TrackEventArgs args)
         {
             if(SourceManager.ActiveSource == o) {
-                playlistModel.RemoveTrack(args.Track);
+                if (args.Track != null) {
+                    RemoveTrackFromModel(args.Track);
+                }
+
+                if (args.Tracks != null) {
+                    if (args.Tracks.Count > 50)
+                        playlistModel.ClearSortOrder();
+
+                    foreach (TrackInfo track in args.Tracks) {
+                        RemoveTrackFromModel(track);
+                    }
+
+                    if (args.Tracks.Count > 50)
+                        playlistModel.RestoreSortOrder();
+                }
             }
+        }
+
+        private void RemoveTrackFromModel(TrackInfo track)
+        {
+            ThreadAssist.ProxyToMain(delegate {
+                playlistModel.RemoveTrack(track);
+            });
         }
         
         private void UpdateViewName(Source source)
Index: src/PlaylistModel.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/PlaylistModel.cs,v
retrieving revision 1.45
diff -u -r1.45 PlaylistModel.cs
--- src/PlaylistModel.cs	31 Oct 2006 18:35:45 -0000	1.45
+++ src/PlaylistModel.cs	27 Nov 2006 07:47:32 -0000
@@ -52,6 +52,9 @@
         
         private RepeatMode repeat = RepeatMode.None;
         private bool shuffle = false;
+
+        private int sort_column;
+        private SortType sort_type;
         
         public event EventHandler Updated;
         public event EventHandler Stopped;
@@ -89,17 +92,22 @@
                 RaiseUpdated(this, new EventArgs());
             }
         }
-        
+
         public void ReloadSource()
         {
             ClearModel();
+
+            ClearSortOrder();
             
             lock(SourceManager.ActiveSource.TracksMutex) {
                 foreach(TrackInfo track in SourceManager.ActiveSource.Tracks) {
                     AddTrack(track, false);
                 }
-                RaiseUpdated(this, new EventArgs());
             }
+
+            RestoreSortOrder();
+
+            RaiseUpdated(this, new EventArgs());
         }
 
         // --- Helper Methods ---
@@ -123,6 +131,18 @@
                 
             return IterTrackInfo(iter);
         }
+
+        public void ClearSortOrder()
+        {
+            GetSortColumnId(out sort_column, out sort_type);
+            SetSortColumnId(-1, SortType.Ascending);
+        }
+
+        public void RestoreSortOrder()
+        {
+            SetSortColumnId(sort_column, sort_type);
+        }
+        
         
         // --- Playback Methods ---
         
@@ -361,7 +381,15 @@
         
         public void RemoveTrack(TrackInfo track)
         {
-        
+            ThreadAssist.ProxyToMain(delegate {
+            TreeIter iter = track.TreeIter;
+
+            if (!iter.Equals(TreeIter.Zero)) {
+                RemoveTrack(ref iter, track);
+            } else {
+                Console.WriteLine ("failed to remove track {0}", track);
+            }
+            });
         }
         
         public int GetIterIndex(TreeIter iter)
Index: src/Banshee.Base/Source.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Source.cs,v
retrieving revision 1.22
diff -u -r1.22 Source.cs
--- src/Banshee.Base/Source.cs	24 Oct 2006 23:14:29 -0000	1.22
+++ src/Banshee.Base/Source.cs	27 Nov 2006 07:47:33 -0000
@@ -47,7 +47,7 @@
     public class TrackEventArgs : EventArgs
     {
         public TrackInfo Track;
-        public IEnumerable Tracks;
+        public ICollection Tracks;
     }
 
     public enum SortCriteria
@@ -149,20 +149,32 @@
         
         public virtual void OnTrackAdded(TrackInfo track)
         {
+            OnTrackAdded(track, null);
+        }
+
+        public virtual void OnTrackAdded(TrackInfo track, ICollection tracks)
+        {
             TrackEventHandler handler = TrackAdded;
             if(handler != null) {
                 TrackEventArgs args = new TrackEventArgs();
                 args.Track = track;
+                args.Tracks = tracks;
                 handler(this, args);
             }
         }
         
         public virtual void OnTrackRemoved(TrackInfo track)
         {
+            OnTrackRemoved(track, null);
+        }
+
+        public virtual void OnTrackRemoved(TrackInfo track, ICollection tracks)
+        {
             TrackEventHandler handler = TrackRemoved;
             if(handler != null) {
                 TrackEventArgs args = new TrackEventArgs();
                 args.Track = track;
+                args.Tracks = tracks;
                 handler(this, args);
             }
         }
Index: src/Banshee.Base/Banshee.SmartPlaylist/Editor.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Banshee.SmartPlaylist/Editor.cs,v
retrieving revision 1.5
diff -u -r1.5 Editor.cs
--- src/Banshee.Base/Banshee.SmartPlaylist/Editor.cs	20 Oct 2006 21:59:56 -0000	1.5
+++ src/Banshee.Base/Banshee.SmartPlaylist/Editor.cs	27 Nov 2006 07:47:44 -0000
@@ -208,6 +208,7 @@
                         playlist.LimitNumber = limit_number;
                         playlist.LimitCriterion = limit_criterion;
                         playlist.Commit();
+
                         playlist.RefreshMembers();
 
                         if (playlist.TimeDependent)
Index: src/Banshee.Base/Banshee.SmartPlaylist/SmartPlaylistSource.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Banshee.SmartPlaylist/SmartPlaylistSource.cs,v
retrieving revision 1.9
diff -u -r1.9 SmartPlaylistSource.cs
--- src/Banshee.Base/Banshee.SmartPlaylist/SmartPlaylistSource.cs	4 Nov 2006 07:54:32 -0000	1.9
+++ src/Banshee.Base/Banshee.SmartPlaylist/SmartPlaylistSource.cs	27 Nov 2006 07:47:44 -0000
@@ -22,8 +22,6 @@
         public string LimitNumber;
         public int LimitCriterion;
 
-        private DateTime start;
-
         private string OrderAndLimit {
             get {
                 if (OrderBy == null || OrderBy == "")
@@ -71,12 +69,14 @@
             get { return ((IList)tracks).SyncRoot; }
         }
 
+        private static string unmap_label = Catalog.GetString ("Delete Smart Playlist");
         public override string UnmapLabel {
-            get { return Catalog.GetString ("Delete Smart Playlist"); }
+            get { return unmap_label; }
         }
 
+        private static string generic_name = Catalog.GetString ("Smart Playlist");
         public override string GenericName {
-            get { return Catalog.GetString ("Smart Playlist"); }
+            get { return generic_name; }
         }
 
         // For existing smart playlists that we're loading from the database
@@ -156,6 +156,11 @@
 
         public void RefreshMembers()
         {
+            RefreshMembers(true);
+        }
+
+        public void RefreshMembers(bool notify)
+        {
             Timer t = new Timer ("RefreshMembers", Name);
 
             //Console.WriteLine ("Refreshing smart playlist {0} with condition {1}", Source.Name, Condition);
@@ -166,11 +171,6 @@
                 "playlist_id", Id
             ));
 
-            foreach (TrackInfo track in tracks)
-                OnTrackRemoved (track);
-
-            tracks.Clear();
-
             // Add matching tracks
             Globals.Library.Db.Execute(String.Format(
                 @"INSERT INTO SmartPlaylistEntries 
@@ -182,38 +182,33 @@
             IDataReader reader = Globals.Library.Db.Query(new DbCommand(
                 @"SELECT TrackID 
                     FROM SmartPlaylistEntries
-                    WHERE PlaylistID = :playlist_id",
+                    WHERE PlaylistID = :playlist_id ORDER BY TrackId",
                     "playlist_id", Id
             ));
             
-            // If the limit is by any but songs, we need to prune the list up based on the desired 
-            // attribute (time/size)
-            if (LimitCriterion == 0 || LimitNumber == "0") {
-                while(reader.Read()) {
-                    if (Globals.Library.Tracks.ContainsKey(Convert.ToInt32(reader[0]))) {
-                        AddTrack (Globals.Library.Tracks[Convert.ToInt32(reader[0])] as TrackInfo);
-                    }
-                }
-            } else {
-                LimitTracks (reader, false);
-            }
-
-            reader.Dispose();
+            List<TrackInfo> tracks_to_add = new List<TrackInfo>();
+            List<TrackInfo> tracks_to_remove = new List<TrackInfo>();
+            Queue<TrackInfo> old_tracks = new Queue<TrackInfo>(tracks);
 
-            t.Stop();
-        }
-
-        private void LimitTracks (IDataReader reader, bool remove_if_limited)
-        {
-            Timer t = new Timer ("LimitTracks", Name);
-
-            bool was_limited = false;
             double sum = 0;
-            double limit = Double.Parse(LimitNumber); 
+            double limit = 0;
+            bool check_limit = LimitCriterion != 0 && LimitNumber != "0";
+            if (check_limit) {
+                limit = Double.Parse(LimitNumber); 
+            }
+
+            int counter = 0;
             while(reader.Read()) {
-                if (Globals.Library.Tracks.ContainsKey(Convert.ToInt32(reader[0]))) {
-                    TrackInfo track = Globals.Library.Tracks[Convert.ToInt32(reader[0])] as TrackInfo;
+                int id = Convert.ToInt32(reader[0]);
+
+                TrackInfo track = Globals.Library.Tracks[id] as TrackInfo;
+                //Console.WriteLine ("evaluating track {0} (old? {1})", track, old_tracks.Contains(track));
+                if (track == null || track.TrackId <= 0) {
+                    Console.WriteLine ("bad track = {0}", track);
+                    continue;
+                }
 
+                if (check_limit) {
                     switch (LimitCriterion) {
                     case 1: // minutes
                         sum += track.Duration.TotalMinutes;
@@ -229,37 +224,44 @@
                         break;
                     }
 
+                    // If we've reached the limit, break out of the add track cycle
                     if (sum > limit) {
-                        was_limited = true;
-
-                        if (remove_if_limited) {
-                            RemoveTrack (track);
-                        } else {
-                            break;
-                        }
-                    } else if (!tracks.Contains (track)) {
-                        AddTrack (track);
+                        break;
                     }
                 }
+
+                if (old_tracks.Count == 0 || id < old_tracks.Peek().TrackId) {
+                    // If we've examined all the old tracks or
+                    // if the new track's ID is less than the old track's, add it
+                    tracks_to_add.Add(track);
+                } else if (id > old_tracks.Peek().TrackId) {
+                    // We know the old track has been removed if this track's ID is greater than it b/c of how we've sorted things
+                    tracks_to_remove.Add(old_tracks.Dequeue());
+
+                    tracks_to_add.Add(track);
+                } else {
+                    // The new track is equal to the old track, so move on to the next old track
+                    old_tracks.Dequeue();
+                }
+                counter++;
             }
 
-            // We do a commit here to clean up the tracks listed in the database..the commit deletes
-            // all Entries for this playlist then inserts them based on one's that are actually in the playlist...
-            // so all the tracks that were beyond the limit point are cleaned out
-            if (was_limited || remove_if_limited)
-                Commit();
+            // If there are old tracks we didn't examine, they should be removed
+            tracks_to_remove.AddRange (old_tracks);
+
+            RemoveTracks(tracks_to_remove, notify);
+            AddTracks(tracks_to_add, notify);
+
+            reader.Dispose();
 
             t.Stop();
         }
 
         public void Check (TrackInfo track)
         {
-            start = DateTime.Now;
-
             if (OrderAndLimit == null) {
                 // If this SmartPlaylist doesn't have an OrderAndLimit clause, then it's quite simple
                 // to check this track - if it matches the Condition we make sure it's in, and vice-versa
-                //Console.WriteLine ("Limitless condition");
                 
                 object id = Globals.Library.Db.QuerySingle(String.Format(
                     "SELECT TrackId FROM Tracks WHERE TrackId = {0} {1}",
@@ -267,55 +269,17 @@
                 ));
 
                 if (id == null || (int) id != track.TrackId) {
+                    // If it didn't match and is in the playlist, remove it
                     if (tracks.Contains (track)) {
-                        // If it didn't match and is in the playlist, remove it
-                        RemoveTrack (track);
+                        RemoveTrack(track);
                     }
                 } else if(! tracks.Contains (track)) {
                     // If it matched and isn't already in the playlist
                     AddTrack (track);
                 }
             } else {
-                // If this SmartPlaylist has an OrderAndLimit clause things are more complicated as there are a limited
-                // number of tracks -- so if we remove a track, we probably need to add a different one and vice-versa.
-                //Console.WriteLine ("Checking track {0} ({1}) against condition & order/limit {2} {3}", track.Uri.LocalPath, track.TrackId, Condition, OrderAndLimit);
-
-                // See if there is a track that was in the SmartPlaylist that now shouldn't be because
-                // this track we are checking displaced it.
-                IDataReader reader = Globals.Library.Db.Query(String.Format(
-                    "SELECT TrackId FROM SmartPlaylistEntries WHERE PlaylistID = {0} " +
-                    "AND TrackId NOT IN (SELECT TrackID FROM Tracks {1} {2})",
-                    Id, PrependCondition("WHERE"), OrderAndLimit
-                ));
-
-                while (reader.Read())
-                    RemoveTrack  (Globals.Library.Tracks[Convert.ToInt32(reader[0])] as TrackInfo);
-
-                reader.Dispose();
-
-                // Remove those tracks from the database
-                Globals.Library.Db.Execute(String.Format(
-                    "DELETE FROM SmartPlaylistEntries WHERE PlaylistID = {0} " +
-                    "AND TrackId NOT IN (SELECT TrackID FROM Tracks {1} {2})",
-                    Id, PrependCondition("WHERE"), OrderAndLimit
-                ));
-
-                // If we are already a member of this smart playlist
-                if (!tracks.Contains (track)) {
-                    // We have removed tracks no longer in this smart playlist, now need to add
-                    // tracks that replace those that were removed (if any), and do limited by size/duration
-                    IDataReader tracks_res = Globals.Library.Db.Query(String.Format(
-                        @"SELECT TrackId FROM Tracks 
-                            WHERE TrackID IN (SELECT TrackID FROM Tracks {1} {2})",
-                        Id, PrependCondition("WHERE"), OrderAndLimit
-                    ));
-
-                    LimitTracks (tracks_res, true);
-
-                    tracks_res.Dispose();
-                }
+                RefreshMembers();
             }
-
         }
 
         public override void Commit ()
@@ -341,27 +305,6 @@
 
             Globals.Library.Db.Execute(command);
 
-            // Make sure the tracks are up to date
-            Globals.Library.Db.Execute(String.Format(
-                @"DELETE FROM SmartPlaylistEntries
-                    WHERE PlaylistID = '{0}'",
-                    id
-            ));
-
-            
-            lock(TracksMutex) {
-                foreach(TrackInfo track in Tracks) {
-                    if(track == null || track.TrackId <= 0)
-                        continue;
-                        
-                    Globals.Library.Db.Execute(String.Format(
-                        @"INSERT INTO SmartPlaylistEntries 
-                            VALUES (NULL, '{0}', '{1}')",
-                            id, track.TrackId
-                    ));
-                }
-            }
-
             t.Stop();
         }
 
@@ -371,19 +314,27 @@
             ed.RunDialog ();
         }
 
-        public override void Reorder(TrackInfo track, int position)
+        // TODO shouldn't try implement this until I understand what is does.
+        /*public override void Reorder(TrackInfo track, int position)
         {
-            RemoveTrack(track);
             lock(TracksMutex) {
                 tracks.Insert(position, track);
             }
+        }*/
+
+        public void AddTracks(List<TrackInfo> tracks_to_add, bool notify)
+        {
+            lock(TracksMutex) {
+                tracks.AddRange(tracks_to_add);
+            }
+
+            if (notify)
+                OnTrackAdded(null, tracks_to_add);
         }
 
         public override void AddTrack(TrackInfo track)
         {
-            //Console.WriteLine ("Adding track ... == null ? {0}", track == null);
             if(track is LibraryTrackInfo) {
-                //Console.WriteLine ("its a LibraryTrackInfo! track = {0}", track);
                 lock(TracksMutex) {
                     tracks.Add(track);
                 }
@@ -391,12 +342,23 @@
                 OnTrackAdded (track);
             }
         }
-        
+
+        public void RemoveTracks(List<TrackInfo> tracks_to_remove, bool notify)
+        {
+            lock(TracksMutex) {
+                foreach (TrackInfo track in tracks_to_remove) {
+                    tracks.Remove(track);
+                }
+            }
+
+            if (notify)
+                OnTrackRemoved(null, tracks_to_remove);
+        }
+
         public override void RemoveTrack(TrackInfo track)
         {
             lock(TracksMutex) {
                 tracks.Remove (track);
-                //  playlistModel.RemoveTrack(ref iters[i], track);
             }
 
             OnTrackRemoved (track);
@@ -440,13 +402,13 @@
             RefreshMembers();
         }
 
+        // TODO if we are a limited smart playlist, we need to fill in the space the
+        // track(s) being removed here have left.  As part of that we should do rate limiting.
         private void OnLibraryTrackRemoved(object o, LibraryTrackRemovedArgs args)
         {
             if(args.Track != null) {
                 if(tracks.Contains(args.Track)) {
                     RemoveTrack(args.Track);
-                    
-                    Commit();
                 }
                 
                 return;
@@ -454,18 +416,11 @@
                 return;
             }
             
-            int removed_count = 0;
-            
             foreach(TrackInfo track in args.Tracks) {
                 if(tracks.Contains(track)) {
                     RemoveTrack (track);
-                    removed_count++;
                 }
             }
-            
-            if(removed_count > 0) {
-                Commit();
-            }
         }
 
         private void HandlePlaylistChanged (object sender, TrackEventArgs args)
@@ -475,7 +430,7 @@
 
             //Console.WriteLine ("{0} sent playlist changed to {1}", (sender as PlaylistSource).Name, Name);
             if (args.Track != null) {
-                start = DateTime.Now;
+                DateTime start = DateTime.Now;
                 Check (args.Track);
                 SmartPlaylistCore.Instance.CpuTime += (DateTime.Now - start).TotalMilliseconds;
             }
@@ -492,6 +447,7 @@
 
             SmartPlaylistSource playlist = new SmartPlaylistSource(id, name, condition, 
                 order_by, limit_number, limit_criterion);
+
             LibrarySource.Instance.AddChildSource(playlist);
 
             if(!SourceManager.ContainsSource (playlist) && SourceManager.ContainsSource(Banshee.Sources.LibrarySource.Instance)) {
