banshee r4677 - in trunk/banshee: . src/Clients/Nereid/Nereid src/Core/Banshee.Core/Banshee.Collection src/Core/Banshee.Services/Banshee.Collection src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Playlist src/Core/Banshee.Services/Banshee.Sources src/Core/Banshee.ThickClient/Banshee.Gui src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio src/Extensions/Banshee.Podcasting/Banshee.Podcasting src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog src/Libraries/Hyena.Gui/Hyena.Data.Gui src/Libraries/Hyena/Hyena.Collections src/Libraries/Hyena/Hyena.Query



Author: gburt
Date: Thu Oct  9 05:11:12 2008
New Revision: 4677
URL: http://svn.gnome.org/viewvc/banshee?rev=4677&view=rev

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

	This patch refactors the Podcast code so that it can have playlists and
	smart playlists.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs:
	Don't assume the PodcastSource is the only podcast-containing source since
	we now have playlists too.

	* src/Core/Banshee.Services/Banshee.Sources/Source.cs: 
	* src/Clients/Nereid/Nereid/PlayerInterface.cs: Add and use new
	Source#GetProperty convenience method.

	* src/Core/Banshee.ThickClient/Banshee.Gui/InterfaceActionService.cs:
	Support propagation of ActiveSource properties, so that podcast playlists
	can have the same actions as the Podcasts source.

	* src/Core/Banshee.ThickClient/Banshee.Gui/BansheeActionGroup.cs: Change
	Add to accept params.

	* src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs: Get rid of cruft, and
	take the Id for a column from the sub-property if it exists, otherwise
	from the Property (as before).

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs:
	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs:
	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastService.cs:
	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs:
	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastActions.cs:
	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog/PodcastPropertiesDialog.cs:
	PodcastTrackInfo no longer subclasses from DatabaseTrackInfo - instead its
	the ExternalObject to the TrackInfos that are under the PodcastSource.
	Change code accessing the PodcastTrackInfo propeties to get to them via
	the PodcastTrackInfo.From (track_info) helper.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs:
	Don't dispose images after rendering them the first time.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs:
	Allow other DatabaseSources (eg PlaylistSources) to be used in this view.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs:
	Allow playlists, set Artwork and ExternalObject handlers, clean up, change
	to reflect PodcastTrackInfo changes, etc.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackListModel.cs:
	Subclass only to set add some default conditions/FROM tables, but no
	longer is a model for/returns PodcastTrackInfo objects.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastFeedModel.cs:
	Change the reload fragment to work with playlists.

	* src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs:
	* src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs:
	New (overridable) DatabaseSource API for creating the TrackModel and
	default Filters for a DatabaseSource.  Child DatabaseSource objects use
	their parent's Create methods so that they get any customizations they
	might have (like Podcasts pulling in other tables/conditions).

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs:
	Override ArtworkId and ExternalObject to use the PrimarySource's handlers,
	if any, to return these values.  Allows us to customize the ArtworkId for
	podcast episodes to be "podcast-$podcasttitle", for instance.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs:
	Allow overriding the default string of table names to select from.

	* src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs: Don't
	accept DnD input from the Play Queue (or any parentless
	non-PrimarySource).

	* src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs: Add
	ExternalObject and ArtworkId delegates that will change the default
	behavior for tracks within this source.

	* src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs: Add a static
	PlaybackFinished event that is fired after play/skip counts are
	incremented.  Used by PodcastTrackInfo to set the Item as not new once
	it's been played.  Add a default (returning null) implementation of
	ExternalObject property.

	* src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs: Call
	DetermineFactor in the static helper so that the factor is set
	appropriately; wasn't called anymore after my recent fix to remember the
	unit the user entered.

	* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs: Allow setting a
	sub-property, either with SubProperty = or by setting Property to a value
	containing a period.  So setting Property to "Foo.Bar" would call the Bar
	getter on the object returning from the Foo getter (called on the object
	bound to the cell, usually a DatabaseTrackInfo in Banshee).  This lets us
	have columns based on properties of, say, the TrackInfo#ExternalObject,
	which we do for Podcasts for Description, PublishedDate, etc.

	* src/Libraries/Hyena/Hyena.Collections/Selection.cs: Expose the
	RangeCollection in a protected getter.

	* src/Core/Banshee.Services/Banshee.Collection/SelectAllSelection.cs: Fix
	bug where we considered any selection of Count 2 that contained the 1st
	item to be 'all'.



Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs
   trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.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/SelectAllSelection.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.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.ThickClient/Banshee.Gui/BansheeActionGroup.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/InterfaceActionService.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
   trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastFeedModel.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackListModel.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastActions.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog/PodcastPropertiesDialog.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastService.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Collections/Selection.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs

Modified: trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs
==============================================================================
--- trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs	(original)
+++ trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs	Thu Oct  9 05:11:12 2008
@@ -333,9 +333,8 @@
             }
             
             // Connect the source models to the views if possible
-            ISourceContents contents = source.GetInheritedProperty<bool> ("Nereid.SourceContentsPropagate")
-                ? source.GetInheritedProperty<ISourceContents> ("Nereid.SourceContents")
-                : source.Properties.Get<ISourceContents> ("Nereid.SourceContents");
+            ISourceContents contents = source.GetProperty<ISourceContents> ("Nereid.SourceContents",
+                source.GetInheritedProperty<bool> ("Nereid.SourceContentsPropagate"));
 
             view_container.ClearFooter ();
             

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs	Thu Oct  9 05:11:12 2008
@@ -57,6 +57,9 @@
     
         public delegate bool IsPlayingHandler (TrackInfo track);
         public static IsPlayingHandler IsPlayingMethod;
+
+        public delegate void PlaybackFinishedHandler (TrackInfo track, double percentComplete);
+        public static event PlaybackFinishedHandler PlaybackFinished;
             
         private SafeUri uri;
         private SafeUri more_info_uri;
@@ -107,12 +110,22 @@
         {
             LastPlayed = DateTime.Now;
             PlayCount++;
+            OnPlaybackFinished (1.0);
         }
 
         public virtual void IncrementSkipCount ()
         {
             LastSkipped = DateTime.Now;
             SkipCount++;
+            OnPlaybackFinished (0.0);
+        }
+
+        private void OnPlaybackFinished (double percentComplete)
+        {
+            PlaybackFinishedHandler handler = PlaybackFinished;
+            if (handler != null) {
+                handler (this, percentComplete);
+            }
         }
 
         public override string ToString ()
@@ -237,6 +250,10 @@
             get { return release_date; }
             set { release_date = value; }
         }        
+
+        public virtual object ExternalObject {
+            get { return null; }
+        }
         
         public string DisplayArtistName { 
             get {
@@ -468,16 +485,21 @@
                 MediaAttributes |= attr;
             }
         }
-        
+
+        // TODO turn this into a PrimarySource-owned delegate?
+        private static string type_podcast = Catalog.GetString ("Podcast");
+        private static string type_video = Catalog.GetString ("Video");
+        private static string type_song = Catalog.GetString ("Song");
+        private static string type_item = Catalog.GetString ("Item");
         public string MediaTypeName {
             get {
                 if (HasAttribute (TrackMediaAttributes.Podcast))
-                    return Catalog.GetString ("Podcast");
+                    return type_podcast;
                 if (HasAttribute (TrackMediaAttributes.VideoStream))
-                    return Catalog.GetString ("Video");
+                    return type_video;
                 if (HasAttribute (TrackMediaAttributes.Music))
-                    return Catalog.GetString ("Song");
-                return Catalog.GetString ("Item");
+                    return type_song;
+                return type_item;
             }
         }
         

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	Thu Oct  9 05:11:12 2008
@@ -106,7 +106,16 @@
             }
             return TrackEqual (this, db_track);
         }
-        
+
+        public override string ArtworkId {
+            get {
+                if (PrimarySource != null && PrimarySource.TrackArtworkIdHandler != null) {
+                    return PrimarySource.TrackArtworkIdHandler (this);
+                }
+                return base.ArtworkId;
+            }
+        }
+
         public static bool TrackEqual (DatabaseTrackInfo a, DatabaseTrackInfo b)
         {
             return a != null && b != null && 
@@ -522,6 +531,16 @@
             get { return external_id; }
             set { external_id = value; }
         }
+
+        private object external_object;
+        public override object ExternalObject {
+            get {
+                if (external_id > 0 && external_object == null && PrimarySource != null && PrimarySource.TrackExternalObjectHandler != null) {
+                    external_object = PrimarySource.TrackExternalObjectHandler (this);
+                }
+                return external_object;
+            }
+        }
         
         [DatabaseColumn ("LastPlayedStamp")]
         public override DateTime LastPlayed {

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	Thu Oct  9 05:11:12 2008
@@ -189,13 +189,19 @@
             get {
                 return unfiltered_query ?? unfiltered_query = String.Format (
                     "FROM {0}{1} WHERE {2} {3}",
-                    provider.From, JoinFragment,
+                    From, JoinFragment,
                     String.IsNullOrEmpty (provider.Where) ? "1=1" : provider.Where,
                     ConditionFragment
                 );
             }
         }
 
+        private string from;
+        protected string From {
+            get { return from ?? provider.From; }
+            set { from = value; }
+        }     
+
         public virtual void UpdateUnfilteredAggregates ()
         {
             HyenaSqliteCommand count_command = new HyenaSqliteCommand (String.Format (

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/SelectAllSelection.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/SelectAllSelection.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/SelectAllSelection.cs	Thu Oct  9 05:11:12 2008
@@ -55,7 +55,19 @@
         }
 
         public override bool AllSelected {
-            get { return Contains (0) || (Count == 2 && Contains (1)); }
+            get {
+                if (Contains (0)) {
+                    return true;
+                }
+
+                // Same as base.AllSelected except range can start at 1 or 0
+                if (RangeCollection.RangeCount == 1) {
+                    RangeCollection.Range range = RangeCollection.Ranges[0];
+                    return range.Start <= 1 && range.End == MaxIndex;
+                }
+
+                return false;
+            }
         }
     }
 }

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	Thu Oct  9 05:11:12 2008
@@ -137,8 +137,9 @@
         public override bool AcceptsInputFromSource (Source source)
         {
             return base.AcceptsInputFromSource (source) && (
-                source == Parent || 
-                (source.Parent == Parent || Parent == null || (source.Parent == null && !(source is PrimarySource)))
+                source == Parent || (source.Parent == Parent || Parent == null)
+                // This is commented out because we don't support (yet?) DnD from Play Queue to a playlist
+                //(source.Parent == Parent || Parent == null || (source.Parent == null && !(source is PrimarySource)))
             );
         }
         
@@ -249,7 +250,7 @@
                 //PrimarySource primary = Parent as PrimarySource;
                 //primary.AddSelectedTracks (model);
                 // then add to us
-                Log.Information ("Note: Feature Not Implemented", String.Format ("In this alpha release, you can only add tracks to {0} from {1} or its playlists.", Name, Parent.Name), true);
+                //Log.Information ("Note: Feature Not Implemented", String.Format ("In this alpha release, you can only add tracks to {0} from {1} or its playlists.", Name, Parent.Name), true);
             }
             return false;
         }

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	Thu Oct  9 05:11:12 2008
@@ -58,15 +58,7 @@
         protected DatabaseTrackListModel track_model;
         protected DatabaseAlbumListModel album_model;
         protected DatabaseArtistListModel artist_model;
-        
-        private DatabaseQueryFilterModel<string> genre_model;
-        protected DatabaseQueryFilterModel<string> GenreModel {
-            get {
-                return genre_model ?? 
-                    genre_model = new Banshee.Collection.Database.DatabaseQueryFilterModel<string> (this, DatabaseTrackModel, ServiceManager.DbConnection,
-                        Catalog.GetString ("All Genres ({0})"), UniqueId, BansheeQuery.GenreField, "Genre");
-            }
-        }
+        protected DatabaseQueryFilterModel<string> genre_model;
 
         private RateLimiter reload_limiter;
 
@@ -106,9 +98,7 @@
         }
 
         public DatabaseTrackListModel DatabaseTrackModel {
-            get {
-                return track_model ?? track_model = new DatabaseTrackListModel (ServiceManager.DbConnection, TrackProvider, this);
-            }
+            get { return track_model ?? track_model = (Parent as DatabaseSource ?? this).CreateTrackModelFor (this); }
             protected set { track_model = value; }
         }
 
@@ -131,22 +121,42 @@
             
             current_filters_schema = CreateSchema<string[]> ("current_filters");
 
-            if (HasArtistAlbum) {
-                artist_model = new DatabaseArtistListModel (this, DatabaseTrackModel, ServiceManager.DbConnection, UniqueId);
-                album_model = new DatabaseAlbumListModel (this, DatabaseTrackModel, ServiceManager.DbConnection, UniqueId);
-                
-                AvailableFilters.Add (GenreModel);
-                AvailableFilters.Add (artist_model);
-                AvailableFilters.Add (album_model);
-                
-                DefaultFilters.Add (GenreModel);
-                DefaultFilters.Add (artist_model);
-                DefaultFilters.Add (album_model);
+            DatabaseSource filter_src = Parent as DatabaseSource ?? this;
+            foreach (IFilterListModel filter in filter_src.CreateFiltersFor (this)) {
+                AvailableFilters.Add (filter);
+                DefaultFilters.Add (filter);
             }
 
             reload_limiter = new RateLimiter (RateLimitedReload);
         }
 
+        protected virtual DatabaseTrackListModel CreateTrackModelFor (DatabaseSource src)
+        {
+            return new DatabaseTrackListModel (ServiceManager.DbConnection, TrackProvider, src);
+        }
+
+        protected virtual IEnumerable<IFilterListModel> CreateFiltersFor (DatabaseSource src)
+        {
+            if (!HasArtistAlbum) {
+                yield break;
+            }
+
+            DatabaseArtistListModel artist_model = new DatabaseArtistListModel (src, src.DatabaseTrackModel, ServiceManager.DbConnection, src.UniqueId);
+            DatabaseAlbumListModel album_model = new DatabaseAlbumListModel (src, src.DatabaseTrackModel, ServiceManager.DbConnection, src.UniqueId);
+            DatabaseQueryFilterModel<string> genre_model = new DatabaseQueryFilterModel<string> (src, src.DatabaseTrackModel, ServiceManager.DbConnection,
+                        Catalog.GetString ("All Genres ({0})"), src.UniqueId, BansheeQuery.GenreField, "Genre");
+
+            if (this == src) {
+                this.artist_model = artist_model;
+                this.album_model = album_model;
+                this.genre_model = genre_model;
+            }
+
+            yield return artist_model;
+            yield return album_model;
+            yield return genre_model;
+        }
+
         protected virtual void AfterInitialized ()
         {
             DatabaseTrackModel.Initialize (TrackCache);

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	Thu Oct  9 05:11:12 2008
@@ -72,14 +72,32 @@
     }
 
     public delegate bool TrackEqualHandler (DatabaseTrackInfo a, TrackInfo b);
+    public delegate object TrackExternalObjectHandler (DatabaseTrackInfo a);
+    public delegate string TrackArtworkIdHandler (DatabaseTrackInfo a);
 
     public abstract class PrimarySource : DatabaseSource, IDisposable
     {
+        #region Functions that let us override some behavior of our DatabaseTrackInfos
+        
         private TrackEqualHandler track_equal_handler;
         public TrackEqualHandler TrackEqualHandler {
             get { return track_equal_handler; }
             protected set { track_equal_handler = value; }
         }
+
+        private TrackExternalObjectHandler track_external_object_handler;
+        public TrackExternalObjectHandler TrackExternalObjectHandler {
+            get { return track_external_object_handler; }
+            protected set { track_external_object_handler = value; }
+        }
+
+        private TrackArtworkIdHandler track_artwork_id_handler;
+        public TrackArtworkIdHandler TrackArtworkIdHandler {
+            get { return track_artwork_id_handler; }
+            protected set { track_artwork_id_handler = value; }
+        }
+
+        #endregion
     
         protected ErrorSource error_source;
         protected bool error_source_visible = false;

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	Thu Oct  9 05:11:12 2008
@@ -242,6 +242,11 @@
                 }
             }
         }
+
+        public T GetProperty<T> (string name, bool propagate)
+        {
+            return propagate ? GetInheritedProperty<T> (name) : Properties.Get<T> (name);
+        }
         
         public T GetInheritedProperty<T> (string name)
         {

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/BansheeActionGroup.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/BansheeActionGroup.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/BansheeActionGroup.cs	Thu Oct  9 05:11:12 2008
@@ -96,7 +96,7 @@
             });
         }
 
-        public new void Add (ActionEntry [] action_entries)
+        public new void Add (params ActionEntry [] action_entries)
         {
             if (ImportantByDefault) {
                 AddImportant (action_entries);

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/InterfaceActionService.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/InterfaceActionService.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/InterfaceActionService.cs	Thu Oct  9 05:11:12 2008
@@ -187,17 +187,19 @@
             if (active_source == null) {
                 return;
             }
-            
-            active_source_actions = active_source.Properties.Get<BansheeActionGroup> ("ActiveSourceActions");
+
+            bool propagate = active_source.GetInheritedProperty<bool> ("ActiveSourceUIResourcePropagate");
+
+            active_source_actions = active_source.GetProperty<BansheeActionGroup> ("ActiveSourceActions", propagate);
             if (active_source_actions != null) {
                 AddActionGroup (active_source_actions);
             }
                 
             Assembly assembly = 
-                active_source.Properties.Get<Assembly> ("ActiveSourceUIResource.Assembly") ??
+                active_source.GetProperty<Assembly> ("ActiveSourceUIResource.Assembly", propagate) ??
                 Assembly.GetAssembly (active_source.GetType ());
 
-            active_source_uiid = AddUiFromFile (active_source.Properties.Get<string> ("ActiveSourceUIResource"), assembly);
+            active_source_uiid = AddUiFromFile (active_source.GetProperty<string> ("ActiveSourceUIResource", propagate), assembly);
         }
 
         public uint AddUiFromFileInCurrentAssembly (string ui_file)

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	Thu Oct  9 05:11:12 2008
@@ -160,14 +160,10 @@
             if (track != null) {
                 this["SeekToAction"].Sensitive = !track.IsLive;
                 this["RestartSongAction"].Sensitive = !track.IsLive;
-                this["RestartSongAction"].Label = (track.MediaAttributes & TrackMediaAttributes.VideoStream) == 0
-                    ? Catalog.GetString ("_Restart Song")
-                    : Catalog.GetString ("_Restart Video");
 
+                this["RestartSongAction"].Label = String.Format (Catalog.GetString ("_Restart {0}"), track.MediaTypeName);
+                this["JumpToPlayingTrackAction"].Label = String.Format (Catalog.GetString ("_Jump to Playing {0}"), track.MediaTypeName);
                 this["JumpToPlayingTrackAction"].Sensitive = true;
-                this["JumpToPlayingTrackAction"].Label = (track.MediaAttributes & TrackMediaAttributes.VideoStream) == 0
-                    ? Catalog.GetString ("_Jump to Playing Song")
-                    : Catalog.GetString ("_Jump to Playing Video");
             } else {
                 this["JumpToPlayingTrackAction"].Sensitive = false;
                 this["RestartSongAction"].Sensitive = false;

Modified: trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	Thu Oct  9 05:11:12 2008
@@ -27,6 +27,7 @@
 //
 
 using System;
+using System.Collections.Generic;
 using Mono.Unix;
 using Gtk;
 
@@ -57,7 +58,6 @@
             TypeUniqueId = "internet-radio";
             IsLocal = false;
             
-            InternetRadioInitialize ();
             AfterInitialized ();
             
             InterfaceActionService uia_service = ServiceManager.Get<InterfaceActionService> ();
@@ -147,17 +147,17 @@
                 Reload ();
             }
         }
-        
-        protected override void Initialize ()
-        {
-            base.Initialize ();
-            InternetRadioInitialize ();
-        }
-        
-        private void InternetRadioInitialize ()
+
+        protected override IEnumerable<IFilterListModel> CreateFiltersFor (DatabaseSource src)
         {
-            AvailableFilters.Add (GenreModel);
-            DefaultFilters.Add (GenreModel);
+            DatabaseQueryFilterModel<string> genre_model = new DatabaseQueryFilterModel<string> (src, src.DatabaseTrackModel, ServiceManager.DbConnection,
+                        Catalog.GetString ("All Genres ({0})"), src.UniqueId, Banshee.Query.BansheeQuery.GenreField, "Genre");
+
+            if (this == src) {
+                this.genre_model = genre_model;
+            }
+
+            yield return genre_model;
         }
         
         public override void Dispose ()

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastFeedModel.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastFeedModel.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastFeedModel.cs	Thu Oct  9 05:11:12 2008
@@ -48,9 +48,10 @@
         {
             ReloadFragmentFormat = @"
                 FROM PodcastSyndications WHERE FeedID IN
-                    (SELECT DISTINCT FeedID FROM PodcastItems, CoreTracks, PodcastEnclosures, CoreCache{0}
-                        WHERE PodcastItems.ItemID = CoreTracks.ExternalID AND PodcastEnclosures.ItemID = PodcastItems.ItemID AND 
-                        CoreCache.ModelID = {1} AND CoreCache.ItemId = {2} {3})
+                    (SELECT DISTINCT PodcastSyndications.FeedID FROM PodcastItems, CoreTracks, PodcastEnclosures, PodcastSyndications, CoreCache{0}
+                        WHERE PodcastSyndications.FeedID = PodcastItems.FeedID AND 
+                          PodcastItems.ItemID = CoreTracks.ExternalID AND PodcastEnclosures.ItemID = PodcastItems.ItemID AND
+                          CoreCache.ModelID = {1} AND CoreCache.ItemId = {2} {3})
                     ORDER BY lower(Title)";
         }
         

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs	Thu Oct  9 05:11:12 2008
@@ -56,8 +56,6 @@
 { 
     public class PodcastSource : Banshee.Library.LibrarySource
     {
-        private PodcastUnheardFilterModel unheard_model;
-        private DownloadStatusFilterModel download_model;
         private PodcastFeedModel feed_model;
 
         private string baseDirectory;
@@ -68,10 +66,9 @@
         public override bool CanRename {
             get { return false; }
         }
-        
-        // FIXME all three of these should be true, eventually
+
         public override bool CanAddTracks {
-            get { return false; }
+            get { return true; }
         }
 
         public override bool CanRemoveTracks {
@@ -96,17 +93,22 @@
         public PodcastSource (string baseDirectory) : base (Catalog.GetString ("Podcasts"), "PodcastLibrary", 200)
         {
             this.baseDirectory = baseDirectory;
+            TrackExternalObjectHandler = GetPodcastInfoObject;
+            TrackArtworkIdHandler = GetTrackArtworkId;
             MediaTypes = TrackMediaAttributes.Podcast;
             NotMediaTypes = TrackMediaAttributes.AudioBook;
             SyncCondition = "(substr(CoreTracks.Uri, 0, 4) != 'http' AND CoreTracks.PlayCount = 0)";
 
-            // For now..
-            SupportsPlaylists = false;
-
             Properties.SetString ("Icon.Name", "podcast");
+            
             Properties.SetString ("ActiveSourceUIResource", "ActiveSourceUI.xml");
+            Properties.Set<bool> ("ActiveSourceUIResourcePropagate", true);
+            Properties.Set<System.Reflection.Assembly> ("ActiveSourceUIResource.Assembly", typeof(PodcastSource).Assembly);
+            
             Properties.SetString ("GtkActionPath", "/PodcastSourcePopup");
+
             Properties.Set<ISourceContents> ("Nereid.SourceContents", new PodcastSourceContents ());
+            Properties.Set<bool> ("Nereid.SourceContentsPropagate", true);
             
             Properties.SetString ("TrackView.ColumnControllerXml", String.Format (@"
                     <column-controller>
@@ -125,7 +127,7 @@
                       <column>
                           <visible>true</visible>
                           <title>{4}</title>
-                          <renderer type=""Hyena.Data.Gui.ColumnCellText"" property=""Description"" />
+                          <renderer type=""Hyena.Data.Gui.ColumnCellText"" property=""ExternalObject.Description"" />
                           <sort-key>Description</sort-key>
                       </column>
                       <column modify-default=""FileSizeColumn"">
@@ -134,19 +136,19 @@
                       <column>
                           <visible>false</visible>
                           <title>{2}</title>
-                          <renderer type=""Banshee.Podcasting.Gui.ColumnCellYesNo"" property=""IsNew"" />
+                          <renderer type=""Banshee.Podcasting.Gui.ColumnCellYesNo"" property=""ExternalObject.IsNew"" />
                           <sort-key>IsNew</sort-key>
                       </column>
                       <column>
                           <visible>false</visible>
                           <title>{3}</title>
-                          <renderer type=""Banshee.Podcasting.Gui.ColumnCellYesNo"" property=""IsDownloaded"" />
+                          <renderer type=""Banshee.Podcasting.Gui.ColumnCellYesNo"" property=""ExternalObject.IsDownloaded"" />
                           <sort-key>IsDownloaded</sort-key>
                       </column>
                       <column>
                           <visible>true</visible>
                           <title>{1}</title>
-                          <renderer type=""Banshee.Podcasting.Gui.ColumnCellPublished"" property=""PublishedDate"" />
+                          <renderer type=""Banshee.Podcasting.Gui.ColumnCellPublished"" property=""ExternalObject.PublishedDate"" />
                           <sort-key>PublishedDate</sort-key>
                       </column>
                       <sort-column direction=""desc"">published_date</sort-column>
@@ -158,42 +160,41 @@
         }
         
 #endregion
+
+        private object GetPodcastInfoObject (DatabaseTrackInfo track)
+        {
+            return new PodcastTrackInfo (track);
+        }
+
+        private string GetTrackArtworkId (DatabaseTrackInfo track)
+        {
+            return PodcastService.ArtworkIdFor (PodcastTrackInfo.From (track).Feed);
+        }
         
         protected override bool HasArtistAlbum {
             get { return false; }
         }
-        
-        protected override void InitializeTrackModel ()
-        {
-            DatabaseTrackModelProvider<PodcastTrackInfo> track_provider =
-                new DatabaseTrackModelProvider<PodcastTrackInfo> (ServiceManager.DbConnection);
 
-            DatabaseTrackModel = new PodcastTrackListModel (ServiceManager.DbConnection, track_provider, this);
+        public override bool AcceptsInputFromSource (Source source)
+        {
+            return false;
+        }
 
-            TrackCache = new DatabaseTrackModelCache<PodcastTrackInfo> (ServiceManager.DbConnection,
-                    UniqueId, track_model, track_provider);
-                    
-            feed_model = new PodcastFeedModel (this, DatabaseTrackModel, ServiceManager.DbConnection, "PodcastFeeds");
-            
-            unheard_model = new PodcastUnheardFilterModel (DatabaseTrackModel);
-            download_model = new DownloadStatusFilterModel (DatabaseTrackModel);
-            
-            AvailableFilters.Add (unheard_model);
-            AvailableFilters.Add (download_model);
-            AvailableFilters.Add (feed_model);
-            
-            DefaultFilters.Add (unheard_model);
-            DefaultFilters.Add (download_model);
-            DefaultFilters.Add (feed_model);
-            
-            AfterInitialized ();
+        protected override DatabaseTrackListModel CreateTrackModelFor (DatabaseSource src)
+        {
+            return new PodcastTrackListModel (ServiceManager.DbConnection, DatabaseTrackInfo.Provider, src);
         }
-        
-        public override void AddChildSource (Source child)
+
+        protected override IEnumerable<IFilterListModel> CreateFiltersFor (DatabaseSource src)
         {
-            Hyena.Log.Information ("Playlists and smart playlists are not supported by the Podcast Library, yet", "", true);
-            if (child is IUnmapableSource) {
-                (child as IUnmapableSource).Unmap ();
+            PodcastFeedModel feed_model;
+            yield return feed_model = new PodcastFeedModel (src, src.DatabaseTrackModel, ServiceManager.DbConnection, String.Format ("PodcastFeeds-{0}", src.UniqueId));
+            yield return new PodcastUnheardFilterModel (src.DatabaseTrackModel);
+            yield return new DownloadStatusFilterModel (src.DatabaseTrackModel);
+
+            if (src == this) {
+                this.feed_model = feed_model;
+                AfterInitialized ();
             }
         }
         
@@ -237,37 +238,6 @@
                 Catalog.GetString ("Unwatched"),
                 Catalog.GetString ("Videos that haven't been played yet"),
                 "plays=0"),
-        };/*
-
-/*
-        public new TrackListModel TrackModel {
-            get { return null; }
-        }
-
-        public override void RemoveSelectedTracks ()
-        {
-        }
-
-        public override void DeleteSelectedTracks ()
-        {
-            throw new InvalidOperationException ();
-        }
-
-        public override bool CanRemoveTracks {
-            get { return false; }
-        }
-
-        public override bool CanDeleteTracks {
-            get { return false; }
-        }
-
-        public override bool ConfirmRemoveTracks {
-            get { return false; }
-        }
-        
-        public override bool ShowBrowser {
-            get { return false; }
-        }
-*/
+        };*/
     }
 }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs	Thu Oct  9 05:11:12 2008
@@ -56,22 +56,36 @@
         None = 7
     }
 
-    public class PodcastTrackInfo : DatabaseTrackInfo
+    public class PodcastTrackInfo
     {
-        private static BansheeModelProvider<PodcastTrackInfo> provider = new DatabaseTrackModelProvider<PodcastTrackInfo> (ServiceManager.DbConnection);
-        public static new BansheeModelProvider<PodcastTrackInfo> Provider {
-            get { return provider; }
+        public static PodcastTrackInfo From (TrackInfo track)
+        {
+            if (track != null) {
+                PodcastTrackInfo pi = track.ExternalObject as PodcastTrackInfo;
+                return pi;
+            }
+            return null;
         }
-        
-        public static PodcastTrackInfo GetByItemId (long item_id)
+
+        public static IEnumerable<PodcastTrackInfo> From (IEnumerable<TrackInfo> tracks)
         {
-            return Provider.FetchFirstMatching ("ExternalID = ?", item_id);
+            foreach (TrackInfo track in tracks) {
+                PodcastTrackInfo pi = From (track);
+                if (pi != null) {
+                    yield return pi;
+                }
+            }
         }
         
         private int position;
+        private DatabaseTrackInfo track;
 
 #region Properties
 
+        public DatabaseTrackInfo Track {
+            get { return track; }
+        }
+
         public Feed Feed {
             get { return Item.Feed; }
         }
@@ -79,12 +93,12 @@
         private FeedItem item;
         public FeedItem Item {
             get {
-                if (item == null && ExternalId > 0) {
-                    item = FeedItem.Provider.FetchSingle (ExternalId);
+                if (item == null && track.ExternalId > 0) {
+                    item = FeedItem.Provider.FetchSingle (track.ExternalId);
                 }
                 return item;
             }
-            set { item = value; ExternalId = value.DbId; }
+            set { item = value; track.ExternalId = value.DbId; }
         }
         
         public DateTime PublishedDate {
@@ -108,22 +122,10 @@
             set { position = value; }
         }
 
-        public override DateTime ReleaseDate {
+        public DateTime ReleaseDate {
             get { return Item.PubDate; }
         }
         
-        //[VirtualDatabaseColumn ("Title", Item.Feed.Title, "ItemID", "ExternalID")]
-        
-        // Override these two so they aren't considered DatabaseColumns so we don't
-        // join with CoreArtists/CoreAlbums
-        /*public override string AlbumTitle {
-            get { return Item.Feed.Title; }
-        }
-        
-        public override string ArtistName {
-            get { return Item.Author; }
-        }*/
-        
         public FeedEnclosure Enclosure {
             get { return (Item == null) ? null : Item.Enclosure; }
         }
@@ -151,41 +153,17 @@
                 }
             }
         }
-        
-        public override string ArtworkId {
-            get { return PodcastService.ArtworkIdFor (Feed); }
-        }
-
-        public string StatusText {
-            get {
-                switch (Activity) {
-                    case PodcastItemActivity.Downloading: return Catalog.GetString ("Downloading");
-                    case PodcastItemActivity.DownloadPending: return Catalog.GetString ("Waiting to download");
-                    case PodcastItemActivity.DownloadPaused: return Catalog.GetString ("Download paused");
-                    case PodcastItemActivity.DownloadFailed: return Catalog.GetString ("Download failed");
-                    default:
-                        string download_status = Activity == PodcastItemActivity.Downloaded
-                            ? Catalog.GetString ("Downloaded")
-                            : Catalog.GetString ("Stream Available");
-                        string new_status = IsNew 
-                            ? Catalog.GetString ("New") 
-                            : ((MediaAttributes & TrackMediaAttributes.VideoStream) != 0
-                                ? Catalog.GetString ("Watched")
-                                : Catalog.GetString ("Heard"));
-                         return String.Format ("{0} / {1}", download_status, new_status);
-                 }
-            }
-        }
 
 #endregion
 
 #region Constructors
     
-        public PodcastTrackInfo () : base ()
+        public PodcastTrackInfo (DatabaseTrackInfo track) : base ()
         {
+            this.track = track;
         }
         
-        public PodcastTrackInfo (FeedItem feed_item) : base ()
+        public PodcastTrackInfo (DatabaseTrackInfo track, FeedItem feed_item) : this (track)
         {
             Item = feed_item;
             SyncWithFeedItem ();
@@ -193,56 +171,46 @@
 
 #endregion
 
-        public void Delete ()
+        static PodcastTrackInfo ()
         {
-            Provider.Delete (this);
+            TrackInfo.PlaybackFinished += OnPlaybackFinished;
         }
-        
-        public override void IncrementPlayCount ()
+
+        private static void OnPlaybackFinished (TrackInfo track, double percentComplete)
         {
-            base.IncrementPlayCount ();
-            
-            if (PlayCount > 0 && !Item.IsRead) {
-                Item.IsRead = true;
-                Item.Save ();
-            }
+            if (percentComplete > 0.5 && track.PlayCount > 0) {
+                PodcastTrackInfo pi = PodcastTrackInfo.From (track);
+                if (pi != null && !pi.Item.IsRead) {
+                    pi.Item.IsRead = true;
+                    pi.Item.Save ();
+                }
+            }   
         }
         
         public void SyncWithFeedItem ()
         {
             //Console.WriteLine ("Syncing item, enclosure == null? {0}", Item.Enclosure == null);
-            ArtistName = Item.Author;
-            AlbumTitle = Item.Feed.Title;
-            TrackTitle = Item.Title;
-            Year = Item.PubDate.Year;
-            CanPlay = true;
-            Genre = Genre ?? "Podcast";
-            ReleaseDate = Item.PubDate;
-            MimeType = Item.Enclosure.MimeType;
-            Duration = Item.Enclosure.Duration;
-            FileSize = Item.Enclosure.FileSize;
-            LicenseUri = Item.LicenseUri;
-            Uri = new Banshee.Base.SafeUri (Item.Enclosure.LocalPath ?? Item.Enclosure.Url);
+            track.ArtistName = Item.Author;
+            track.AlbumTitle = Item.Feed.Title;
+            track.TrackTitle = Item.Title;
+            track.Year = Item.PubDate.Year;
+            track.CanPlay = true;
+            track.Genre = track.Genre ?? "Podcast";
+            track.ReleaseDate = Item.PubDate;
+            track.MimeType = Item.Enclosure.MimeType;
+            track.Duration = Item.Enclosure.Duration;
+            track.FileSize = Item.Enclosure.FileSize;
+            track.LicenseUri = Item.LicenseUri;
+            track.Uri = new Banshee.Base.SafeUri (Item.Enclosure.LocalPath ?? Item.Enclosure.Url);
             
             if (!String.IsNullOrEmpty (Item.Enclosure.LocalPath)) {
                 try {
-                    TagLib.File file = Banshee.Streaming.StreamTagger.ProcessUri (Uri);
-                    Banshee.Streaming.StreamTagger.TrackInfoMerge (this, file, true);
+                    TagLib.File file = Banshee.Streaming.StreamTagger.ProcessUri (track.Uri);
+                    Banshee.Streaming.StreamTagger.TrackInfoMerge (track, file, true);
                 } catch {}
             }
 
-            MediaAttributes |= TrackMediaAttributes.Podcast;
-        }
-        
-        protected override void ProviderSave ()
-        {
-            MediaAttributes |= TrackMediaAttributes.Podcast;
-            Provider.Save (this);
-        }
-        
-        protected override bool ProviderRefresh ()
-        {
-            return Provider.Refresh (this);
+            track.MediaAttributes |= TrackMediaAttributes.Podcast;
         }
     }
 }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackListModel.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackListModel.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackListModel.cs	Thu Oct  9 05:11:12 2008
@@ -54,16 +54,15 @@
 
 namespace Banshee.Podcasting.Gui
 {
-    public class PodcastTrackListModel : DatabaseTrackListModel, IListModel<PodcastTrackInfo>
+    public class PodcastTrackListModel : DatabaseTrackListModel
     {
         public PodcastTrackListModel (BansheeDbConnection conn, IDatabaseTrackModelProvider provider, DatabaseSource source) : base (conn, provider, source)
         {
-            JoinTable = String.Format ("{0}, {1}, {2}", Feed.Provider.TableName, FeedItem.Provider.TableName, FeedEnclosure.Provider.TableName);
-            JoinPrimaryKey = FeedItem.Provider.PrimaryKey;
-            JoinColumn = "ExternalID";
+            From = String.Format ("{0}, {1}, {2}, {3}", provider.From, Feed.Provider.TableName, FeedItem.Provider.TableName, FeedEnclosure.Provider.TableName);
+            int podcast_dbid = (source as PodcastSource ?? source.Parent as PodcastSource).DbId;
             AddCondition (String.Format (
-                "{0}.FeedID = {1}.FeedID AND CoreTracks.ExternalID = {1}.ItemID AND {1}.ItemID = {2}.ItemID",
-                Feed.Provider.TableName, FeedItem.Provider.TableName, FeedEnclosure.Provider.TableName
+                "CoreTracks.PrimarySourceID = {3} AND {0}.FeedID = {1}.FeedID AND CoreTracks.ExternalID = {1}.ItemID AND {1}.ItemID = {2}.ItemID",
+                Feed.Provider.TableName, FeedItem.Provider.TableName, FeedEnclosure.Provider.TableName, podcast_dbid
             ));
         }
 
@@ -110,13 +109,5 @@
 
             return sort_query ?? Banshee.Query.BansheeQuery.GetSort (key, asc);
         }
-        
-        public new PodcastTrackInfo this[int index] {
-            get {
-                lock (this) {
-                    return cache.GetValue (index) as PodcastTrackInfo;
-                }
-            }
-        }
     }
 }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcast.cs	Thu Oct  9 05:11:12 2008
@@ -51,7 +51,7 @@
         
         // TODO replace this w/ new icon installation etc
         private static ImageSurface default_cover_image 
-            = new PixbufImageSurface (IconThemeUtils.LoadIcon (48, "podcast"));
+            = new PixbufImageSurface (IconThemeUtils.LoadIcon (image_size, "podcast"));
         
         private ArtworkManager artwork_manager;
 
@@ -72,7 +72,7 @@
             
             Feed feed = (Feed)BoundObject;
             
-            bool is_default = false;          
+            bool is_default = false;
             ImageSurface image = artwork_manager == null ? null 
                 : artwork_manager.LookupScaleSurface (PodcastService.ArtworkIdFor (feed), image_size, true);
             
@@ -86,7 +86,7 @@
             int x = image_spacing;
             int y = ((int)cellHeight - image_render_size) / 2;
 
-            ArtworkRenderer.RenderThumbnail (context.Context, image, !is_default, x, y, 
+            ArtworkRenderer.RenderThumbnail (context.Context, image, false, x, y, 
                 image_render_size, image_render_size, !is_default, context.Theme.Context.Radius);
                 
             int fl_width = 0, fl_height = 0, sl_width = 0, sl_height = 0;

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs	Thu Oct  9 05:11:12 2008
@@ -65,7 +65,7 @@
         
         protected override int GetIconIndex (TrackInfo track)
         {
-            PodcastTrackInfo podcast = track as PodcastTrackInfo;
+            PodcastTrackInfo podcast = PodcastTrackInfo.From (track);
             if (track == null) {
                 return -1;
             }
@@ -81,7 +81,7 @@
         
         public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
         {
-            PodcastTrackInfo podcast = BoundTrack as PodcastTrackInfo;
+            PodcastTrackInfo podcast = PodcastTrackInfo.From (BoundTrack);
             if (podcast != null) {
                 if (podcast.Activity == PodcastItemActivity.DownloadPending) {
                     context.Sensitive = false;

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastActions.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastActions.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastActions.cs	Thu Oct  9 05:11:12 2008
@@ -56,13 +56,11 @@
     public class PodcastActions : BansheeActionGroup
     {
         private uint actions_id;
-        private PodcastSource source;
+        private DatabaseSource last_source;
         
         public PodcastActions (PodcastSource source) : base (ServiceManager.Get<InterfaceActionService> (), "Podcast")
         {
-            this.source = source;
-
-            AddImportant (new ActionEntry[] {
+            AddImportant (
                 new ActionEntry (
                     "PodcastUpdateAllAction", Stock.Refresh,
                      Catalog.GetString ("Update Podcasts"), null,//"<control><shift>U",
@@ -75,9 +73,9 @@
                      Catalog.GetString ("Subscribe to a new podcast"),
                      OnPodcastAdd
                 )         
-            });
+            );
             
-            Add (new ActionEntry [] {
+            Add (
                 new ActionEntry("PodcastFeedPopupAction", null, 
                     String.Empty, null, null, OnFeedPopup),
                     
@@ -154,18 +152,13 @@
                      null, String.Empty, 
                      OnPodcastItemProperties
                 )
-            });
+            );
             
             actions_id = Actions.UIManager.AddUiFromResource ("GlobalUI.xml");
             Actions.AddActionGroup (this);
 
             ServiceManager.SourceManager.ActiveSourceChanged += HandleActiveSourceChanged;
-            
-            source.TrackModel.Selection.Changed += delegate { Banshee.Base.ThreadAssist.ProxyToMain (UpdateItemActions); };
-            source.FeedModel.Selection.Changed += delegate { Banshee.Base.ThreadAssist.ProxyToMain (UpdateFeedActions); };
-            
-            UpdateFeedActions ();
-            UpdateItemActions ();
+            OnSelectionChanged (null, null);
         }
 
         public override void Dispose ()
@@ -176,9 +169,34 @@
         }
 
 #region State Event Handlers
-
+        
         private void HandleActiveSourceChanged (SourceEventArgs args)
         {
+            if (last_source != null) {
+                foreach (IFilterListModel filter in last_source.AvailableFilters) {
+                    filter.Selection.Changed -= OnSelectionChanged;
+                }
+                last_source.TrackModel.Selection.Changed -= OnSelectionChanged;
+                last_source = null;
+            }
+
+            last_source = args.Source as DatabaseSource;
+            if (IsPodcastSource) {
+                if (last_source != null) {
+                    foreach (IFilterListModel filter in last_source.AvailableFilters) {
+                        filter.Selection.Changed += OnSelectionChanged;
+                    }
+                    last_source.TrackModel.Selection.Changed += OnSelectionChanged;
+                }
+            } else {
+                last_source = null;
+            }
+
+            OnSelectionChanged (null, null);
+        }
+
+        private void OnSelectionChanged (object o, EventArgs args)
+        {
             Banshee.Base.ThreadAssist.ProxyToMain (delegate {
                 UpdateFeedActions ();
                 UpdateItemActions ();
@@ -189,10 +207,37 @@
 
 #region Utility Methods
 
+        private DatabaseSource ActiveDbSource {
+            get { return last_source; }
+        }
+
+        private bool IsPodcastSource {
+            get {
+                return ActiveDbSource != null && (ActiveDbSource is PodcastSource || ActiveDbSource.Parent is PodcastSource);
+            }
+        }
+
+        public PodcastFeedModel ActiveFeedModel {
+            get {
+                if (ActiveDbSource == null) {
+                    return null;
+                } else if (ActiveDbSource is PodcastSource) {
+                    return (ActiveDbSource as PodcastSource).FeedModel;
+                } else {
+                    foreach (IFilterListModel filter in ActiveDbSource.AvailableFilters) {
+                        if (filter is PodcastFeedModel) {
+                            return filter as PodcastFeedModel;
+                        }
+                    }
+                    return null;
+                }
+            }
+        }
+
         private void UpdateItemActions ()
         {
-            if (ServiceManager.SourceManager.ActiveSource == source) {
-                bool has_single_selection = source.TrackModel.Selection.Count == 1;
+            if (IsPodcastSource) {
+                bool has_single_selection = ActiveDbSource.TrackModel.Selection.Count == 1;
                 UpdateActions (true, has_single_selection,
                    "PodcastItemLinkAction"
                 );
@@ -201,9 +246,9 @@
         
         private void UpdateFeedActions ()
         {
-            if (ServiceManager.SourceManager.ActiveSource == source) {
-                bool has_single_selection = source.FeedModel.Selection.Count == 1;
-                bool all_selected = source.FeedModel.Selection.AllSelected;
+            if (IsPodcastSource) {
+                bool has_single_selection = ActiveFeedModel.Selection.Count == 1;
+                bool all_selected = ActiveFeedModel.Selection.AllSelected;
 
                 UpdateActions (true, has_single_selection && !all_selected,
                     "PodcastDeleteAction", "PodcastUpdateFeedAction", "PodcastHomepageAction",
@@ -219,19 +264,21 @@
 
         private IEnumerable<TrackInfo> GetSelectedItems ()
         {
-            return new List<TrackInfo> (source.TrackModel.SelectedItems);
+            return new List<TrackInfo> (ActiveDbSource.TrackModel.SelectedItems);
         }
 
 #endregion
-            
+
+                
 #region Action Handlers
 
         private void OnFeedPopup (object o, EventArgs args)
         {
-            if (source.FeedModel.Selection.AllSelected)
+            if (ActiveFeedModel.Selection.AllSelected) {
                 ShowContextMenu ("/PodcastAllFeedsContextMenu");
-            else
+            } else {
                 ShowContextMenu ("/PodcastFeedPopup");
+            }
         }
 
         private void RunSubscribeDialog ()
@@ -375,7 +422,7 @@
         
         private void OnPodcastUpdate (object sender, EventArgs e)
         {
-            foreach (Feed feed in source.FeedModel.SelectedItems) {
+            foreach (Feed feed in ActiveFeedModel.SelectedItems) {
                 feed.Update ();
             }
         }        
@@ -389,7 +436,7 @@
         
         private void OnPodcastDelete (object sender, EventArgs e)
         {
-            Feed feed = source.FeedModel.FocusedItem;
+            Feed feed = ActiveFeedModel.FocusedItem;
             if (feed != null) {
                 feed.Delete (true);
             }
@@ -397,7 +444,7 @@
         
         private void OnPodcastDownloadAllEpisodes (object sender, EventArgs e)
         {
-            Feed feed = source.FeedModel.FocusedItem;
+            Feed feed = ActiveFeedModel.FocusedItem;
             if (feed != null) {
                 foreach (FeedItem item in feed.Items) {
                     item.Enclosure.AsyncDownload ();
@@ -407,15 +454,15 @@
 
         private void OnPodcastItemDeleteFile (object sender, EventArgs e)
         {
-            foreach (PodcastTrackInfo pi in GetSelectedItems ()) {
+            foreach (PodcastTrackInfo pi in PodcastTrackInfo.From (GetSelectedItems ())) {
                 if (pi.Enclosure.LocalPath != null)
                     pi.Enclosure.DeleteFile ();
             }
-        }  
+        }
 
         private void OnPodcastHomepage (object sender, EventArgs e)
         {
-            Feed feed = source.FeedModel.FocusedItem;
+            Feed feed = ActiveFeedModel.FocusedItem;
             if (feed != null && !String.IsNullOrEmpty (feed.Link)) {
                 Banshee.Web.Browser.Open (feed.Link);
             }   
@@ -423,7 +470,7 @@
 
         private void OnPodcastProperties (object sender, EventArgs e)
         {
-            Feed feed = source.FeedModel.FocusedItem;
+            Feed feed = ActiveFeedModel.FocusedItem;
             if (feed != null) {
                 new PodcastFeedPropertiesDialog (feed).Run ();
             }
@@ -450,19 +497,19 @@
         
         private void MarkPodcastItemSelection (bool markRead) 
         {
-            TrackInfo new_selection_track = source.TrackModel [source.TrackModel.Selection.LastIndex + 1];
+            TrackInfo new_selection_track = ActiveDbSource.TrackModel [ActiveDbSource.TrackModel.Selection.LastIndex + 1];
             
             PodcastService.IgnoreItemChanges = true;
             
             bool any = false;
-            foreach (PodcastTrackInfo track in GetSelectedItems ()) {
+            foreach (PodcastTrackInfo track in PodcastTrackInfo.From (GetSelectedItems ())) {
                 if (track.Item.IsRead != markRead) {
                     track.Item.IsRead = markRead;
                     track.Item.Save ();
 
-                    if (track.Item.IsRead ^ track.PlayCount > 0) {
-                        track.PlayCount = track.Item.IsRead ? 1 : 0;
-                        track.Save (false);
+                    if (track.Item.IsRead ^ track.Track.PlayCount > 0) {
+                        track.Track.PlayCount = track.Item.IsRead ? 1 : 0;
+                        track.Track.Save (false);
                     }
                     any = true;
                 }
@@ -471,16 +518,16 @@
             PodcastService.IgnoreItemChanges = false;
             
             if (any) {
-                source.Reload ();
+                ActiveDbSource.Reload ();
                 
                 // If we just removed all of the selected items from our view, we should select the
                 // item after the last removed item
-                if (source.TrackModel.Selection.Count == 0 && new_selection_track != null) {
-                    int new_i = source.TrackModel.IndexOf (new_selection_track);
+                if (ActiveDbSource.TrackModel.Selection.Count == 0 && new_selection_track != null) {
+                    int new_i = ActiveDbSource.TrackModel.IndexOf (new_selection_track);
                     if (new_i != -1) {
-                        source.TrackModel.Selection.Clear (false);
-                        source.TrackModel.Selection.FocusedIndex = new_i;
-                        source.TrackModel.Selection.Select (new_i);
+                        ActiveDbSource.TrackModel.Selection.Clear (false);
+                        ActiveDbSource.TrackModel.Selection.FocusedIndex = new_i;
+                        ActiveDbSource.TrackModel.Selection.Select (new_i);
                     }
                 }
             }
@@ -502,7 +549,7 @@
         
         private void OnPodcastItemDownload (object sender, EventArgs e)
         {
-            foreach (PodcastTrackInfo pi in GetSelectedItems ()) {
+            foreach (PodcastTrackInfo pi in PodcastTrackInfo.From (GetSelectedItems ())) {
                 if (pi.Enclosure.DownloadStatus != FeedDownloadStatus.Downloaded)
                     pi.Enclosure.AsyncDownload ();
             }
@@ -510,7 +557,7 @@
         
         private void OnPodcastItemLink (object sender, EventArgs e)
         {
-            PodcastTrackInfo track = source.TrackModel.FocusedItem as PodcastTrackInfo;
+            PodcastTrackInfo track = PodcastTrackInfo.From (ActiveDbSource.TrackModel.FocusedItem);
             if (track != null && !String.IsNullOrEmpty (track.Item.Link)) {
                 Banshee.Web.Browser.Open (track.Item.Link);
             }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs	Thu Oct  9 05:11:12 2008
@@ -60,7 +60,7 @@
                 return;
             }
             
-            PodcastTrackInfo podcast = boundItem as PodcastTrackInfo;
+            PodcastTrackInfo podcast = PodcastTrackInfo.From (boundItem as TrackInfo);
             if (podcast == null) {
                 return;
             }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog/PodcastPropertiesDialog.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog/PodcastPropertiesDialog.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog/PodcastPropertiesDialog.cs	Thu Oct  9 05:11:12 2008
@@ -34,6 +34,7 @@
 using Pango;
 
 using Banshee.Podcasting.Data;
+using Banshee.Collection.Database;
 
 namespace Banshee.Podcasting.Gui
 {
@@ -41,8 +42,9 @@
     {
         private PodcastTrackInfo pi;
 
-        public PodcastPropertiesDialog (PodcastTrackInfo pi)
+        public PodcastPropertiesDialog (DatabaseTrackInfo track)
         {
+            PodcastTrackInfo pi = PodcastTrackInfo.From (track);
             if (pi == null)
             {
                 throw new ArgumentNullException ("pi");
@@ -50,7 +52,7 @@
             
             this.pi = pi;            
             
-            Title = pi.TrackTitle;
+            Title = track.TrackTitle;
             BuildWindow ();
             //IconThemeUtils.SetWindowIcon (this);
         }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs	Thu Oct  9 05:11:12 2008
@@ -81,11 +81,8 @@
 
         protected override bool ActiveSourceCanHasBrowser {
             get {
-                if (!(ServiceManager.SourceManager.ActiveSource is PodcastSource)) {
-                    return false;
-                }
-                
-                return ((PodcastSource)ServiceManager.SourceManager.ActiveSource).ShowBrowser;
+                DatabaseSource db_src = ServiceManager.SourceManager.ActiveSource as DatabaseSource;
+                return db_src != null && db_src.ShowBrowser;
             }
         }
 
@@ -94,7 +91,7 @@
         public override bool SetSource (ISource source)
         {
             //Console.WriteLine ("PSC.set_source 1");
-            PodcastSource track_source = source as PodcastSource;
+            DatabaseSource track_source = source as DatabaseSource;
             if (track_source == null) {
                 return false;
             }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs	Thu Oct  9 05:11:12 2008
@@ -80,8 +80,9 @@
             }
             
             if (SaveHttpStreamCover (new Uri (feed.ImageUrl), cover_art_id, null)) {
-                if (ServiceManager.SourceManager.ActiveSource is PodcastSource) {
-                    (ServiceManager.SourceManager.ActiveSource as PodcastSource).Reload ();
+                Banshee.Sources.Source src = ServiceManager.SourceManager.ActiveSource;
+                if (src != null && (src is PodcastSource || src.Parent is PodcastSource)) {
+                    (src as Banshee.Sources.DatabaseSource).Reload ();
                 }
                 return;
             }

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastService.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastService.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastService.cs	Thu Oct  9 05:11:12 2008
@@ -148,11 +148,11 @@
                         );
 
                         if (track_id > 0) {
-                            PodcastTrackInfo track = PodcastTrackInfo.Provider.FetchSingle (track_id);
-                            track.Item = enclosure.Item;
-                            track.PrimarySourceId = source.DbId;
-                            track.SyncWithFeedItem ();
-                            track.Save (false);
+                            PodcastTrackInfo pi = new PodcastTrackInfo (DatabaseTrackInfo.Provider.FetchSingle (track_id));
+                            pi.Item = enclosure.Item;
+                            pi.Track.PrimarySourceId = source.DbId;
+                            pi.SyncWithFeedItem ();
+                            pi.Track.Save (false);
                             moved++;
                         }
                     }
@@ -322,12 +322,19 @@
                 Banshee.Kernel.Scheduler.Schedule (new PodcastImageFetchJob (feed), Banshee.Kernel.JobPriority.BelowNormal);
             }
         }
+
+        private DatabaseTrackInfo GetTrackByItemId (long item_id)
+        {
+            return DatabaseTrackInfo.Provider.FetchFirstMatching ("PrimarySourceID = ? AND ExternalID = ?", source.DbId, item_id);
+        }
         
         private void OnItemAdded (FeedItem item)
         {
             if (item.Enclosure != null) {
-                PodcastTrackInfo track = new PodcastTrackInfo (item);
+                DatabaseTrackInfo track = new DatabaseTrackInfo ();
+                track.ExternalId = item.DbId;
                 track.PrimarySource = source;
+                (track.ExternalObject as PodcastTrackInfo).SyncWithFeedItem ();
                 track.Save (true);
                 RefreshArtworkFor (item.Feed);
             } else {
@@ -338,9 +345,9 @@
         
         private void OnItemRemoved (FeedItem item)
         {
-            PodcastTrackInfo track = PodcastTrackInfo.GetByItemId (item.DbId);
+            DatabaseTrackInfo track = GetTrackByItemId (item.DbId);
             if (track != null) {
-                track.Delete ();
+                DatabaseTrackInfo.Provider.Delete (track);
             }
         }
         
@@ -348,13 +355,17 @@
         
         private void OnItemChanged (FeedItem item)
         {
-            if (IgnoreItemChanges)
+            if (IgnoreItemChanges) {
                 return;
+            }
 
-            PodcastTrackInfo track = PodcastTrackInfo.GetByItemId (item.DbId);
+            DatabaseTrackInfo track = GetTrackByItemId (item.DbId);
             if (track != null) {
-                track.SyncWithFeedItem ();
-                track.Save (true);
+                PodcastTrackInfo pi = track.ExternalObject as PodcastTrackInfo;
+                if (pi != null) {
+                    pi.SyncWithFeedItem ();
+                    track.Save (true);
+                }
             }
         }
         
@@ -415,9 +426,9 @@
         public void AddFeedItem (FeedItem item)
         {
             if (item.Enclosure != null) {
-                PodcastTrackInfo pi = new PodcastTrackInfo (item);
-                pi.PrimarySource = source;
-                pi.Save (true);
+                PodcastTrackInfo pi = new PodcastTrackInfo (new DatabaseTrackInfo (), item);
+                pi.Track.PrimarySource = source;
+                pi.Track.Save (true);
                 source.NotifyUser ();
             } else {
                 item.Delete (false);                      

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs	Thu Oct  9 05:11:12 2008
@@ -43,8 +43,6 @@
         
         private int minWidth = 0;
         private int maxWidth = Int32.MaxValue;
-        //private double minRelativeWidth = 0;
-        //private double relativeWidth = 0;
         
         public Column (ColumnDescription description) :
             this (description, new ColumnCellText (description.Property, true))
@@ -176,11 +174,6 @@
                 }
             }
         }
-
-        /*internal double MinRelativeWidth {
-            get { return minRelativeWidth; }
-            set { minRelativeWidth = value; }
-        }*/
         
         public int MaxWidth {
             get { return maxWidth; }
@@ -192,14 +185,15 @@
             }
         }
 
-        /*public double RelativeWidth {
-            get { return relativeWidth; }
-            set { relativeWidth = value; }
-        }*/
-
         private string id;
         public string Id {
-            get { return id ?? id = StringUtil.CamelCaseToUnderCase (GetCell (0).Property); }
+            get {
+                if (id == null) {
+                    id = GetCell (0).SubProperty ?? GetCell (0).Property;
+                    id = StringUtil.CamelCaseToUnderCase (id);
+                }
+                return id;
+            }
             set { id = value; }
         }
     }

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs	Thu Oct  9 05:11:12 2008
@@ -36,15 +36,15 @@
     public abstract class ColumnCell
     {
         private bool expand;
-        private string property;
-        private PropertyInfo property_info;
+        private string property, sub_property;
+        private PropertyInfo property_info, sub_property_info;
         private object bound_object;
         private object bound_object_parent;
             
         public ColumnCell (string property, bool expand)
         {
-            this.property = property;
-            this.expand = expand;
+            Property = property;
+            Expand = expand;
         }
 
         public void BindListItem (object item)
@@ -60,19 +60,29 @@
             if (property != null) {
                 EnsurePropertyInfo ();
                 bound_object = property_info.GetValue (bound_object_parent, null);
+
+                if (sub_property != null) {
+                    EnsurePropertyInfo (sub_property, ref sub_property_info, bound_object);
+                    bound_object = sub_property_info.GetValue (bound_object, null);
+                }
             } else {
                 bound_object = bound_object_parent;
             }
         }
-        
+
         private void EnsurePropertyInfo ()
         {
-            if (property_info == null || property_info.ReflectedType != bound_object_parent.GetType ()) {
-                property_info = bound_object_parent.GetType ().GetProperty (property);
-                if (property_info == null) {
+            EnsurePropertyInfo (property, ref property_info, bound_object_parent);
+        }
+        
+        private void EnsurePropertyInfo (string name, ref PropertyInfo prop, object obj)
+        {
+            if (prop == null || prop.ReflectedType != obj.GetType ()) {
+                prop = obj.GetType ().GetProperty (name);
+                if (prop == null) {
                     throw new Exception (String.Format (
                         "In {0}, type {1} does not have property {2}",
-                        this, bound_object_parent.GetType (), property
+                        this, obj.GetType (), name
                     ));
                 }
             }
@@ -109,7 +119,21 @@
         
         public string Property {
             get { return property; }
-            set { property = value; }
+            set {
+                property = value;
+                if (value != null) {
+                    int i = value.IndexOf (".");
+                    if (i != -1) {
+                        property = value.Substring (0, i);
+                        SubProperty = value.Substring (i + 1, value.Length - i - 1);
+                    }
+                }
+            }
+        }
+
+        public string SubProperty {
+            get { return sub_property; }
+            set { sub_property = value; }
         }
     }
 }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Collections/Selection.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Collections/Selection.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Collections/Selection.cs	Thu Oct  9 05:11:12 2008
@@ -183,6 +183,10 @@
             }
         }
 
+        protected RangeCollection RangeCollection {
+            get { return ranges; }
+        }
+
         public RangeCollection.Range [] Ranges {
             get { return ranges.Ranges; }
         }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Query/RelativeTimeSpanQueryValue.cs	Thu Oct  9 05:11:12 2008
@@ -55,6 +55,7 @@
         {
             RelativeTimeSpanQueryValue qv = new RelativeTimeSpanQueryValue ();
             qv.SetRelativeValue ((since - DateTime.Now).TotalSeconds, TimeFactor.Second);
+            qv.DetermineFactor ();
             return qv;
         }
 



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