[banshee] Play queue enhancements and fixes (bgo#565767, bgo#553399, bgo#553684)



commit e5fe5800dbf5ae7eda3ba4e2616d92b8387aae19
Author: Alexander Kojevnikov <alexander kojevnikov com>
Date:   Thu Sep 10 11:40:12 2009 +1000

    Play queue enhancements and fixes (bgo#565767, bgo#553399, bgo#553684)
    
    New features:
    
     * Songs are never removed from the play queue.
     * Option to control the number of played songs to show.
     * Option to randomly populate the play queue from a playlist
       using any shuffle mode.
     * Option to control the number of upcoming songs.
     * Played tracks are rendered with a different opacity.
     * Manually added songs are always inserted before the randomly added ones.

 src/Clients/Nereid/Nereid/PlayerInterface.cs       |   10 +
 src/Clients/Nereid/Nereid/ViewContainer.cs         |   23 +-
 .../Banshee.Core/Banshee.Collection/TrackInfo.cs   |    6 +
 .../Banshee.Configuration/SchemaEntry.cs           |   20 +-
 .../DatabaseTrackListModel.cs                      |    2 +-
 .../Banshee.Database/BansheeDbFormatMigrator.cs    |   18 +-
 .../Banshee.Playlist/PlaylistSource.cs             |    2 +-
 .../Banshee.Preferences/SchemaPreference.cs        |   12 +-
 .../Banshee.Collection.Gui/BaseTrackListView.cs    |    2 +-
 .../ColumnCellCreativeCommons.cs                   |    4 +-
 .../ColumnCellStatusIndicator.cs                   |    4 +-
 .../Banshee.Collection.Gui/ColumnCellTrack.cs      |    2 +-
 .../Banshee.Preferences.Gui/WidgetFactory.cs       |   32 +
 .../Banshee.Sources.Gui/SourceRowRenderer.cs       |   12 +
 .../Banshee.PlayQueue/Banshee.PlayQueue.addin.xml  |    2 +-
 .../Banshee.PlayQueue/Banshee.PlayQueue.csproj     |    3 +
 .../Banshee.PlayQueue/HeaderWidget.cs              |  128 ++++
 .../Banshee.PlayQueue/PlayQueueActions.cs          |   16 +-
 .../Banshee.PlayQueue/PlayQueueSource.cs           |  633 +++++++++++++++++---
 .../Banshee.PlayQueue/PlayQueueTrackListModel.cs   |   67 ++
 .../Banshee.PlayQueue/QueueableSourceComboBox.cs   |  106 ++++
 src/Extensions/Banshee.PlayQueue/Makefile.am       |    5 +-
 .../Banshee.PlayQueue/Resources/ActiveSourceUI.xml |    1 +
 .../Banshee.PlayQueue/Resources/GlobalUI.xml       |    2 +
 .../Banshee.Podcasting.Data/PodcastSource.cs       |   10 +-
 .../Banshee.Podcasting.Data/PodcastTrackInfo.cs    |    1 +
 .../ColumnCellPodcastStatusIndicator.cs            |    2 +-
 .../Banshee.Podcasting.Gui/PodcastItemView.cs      |   21 -
 .../PodcastSourceContents.cs                       |    4 +-
 .../Hyena.Gui/Hyena.Data.Gui/CellContext.cs        |   10 +-
 .../Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs     |    8 +-
 .../Hyena.Data.Gui/ListView/ListView_Model.cs      |   59 +-
 .../Hyena.Data.Gui/ListView/ListView_Rendering.cs  |   12 +-
 src/Libraries/Hyena/Hyena.Collections/Selection.cs |   13 +
 34 files changed, 1075 insertions(+), 177 deletions(-)
---
diff --git a/src/Clients/Nereid/Nereid/PlayerInterface.cs b/src/Clients/Nereid/Nereid/PlayerInterface.cs
index 1b95574..73b8216 100644
--- a/src/Clients/Nereid/Nereid/PlayerInterface.cs
+++ b/src/Clients/Nereid/Nereid/PlayerInterface.cs
@@ -346,6 +346,7 @@ namespace Nereid
             ISourceContents contents = source.GetProperty<ISourceContents> ("Nereid.SourceContents",
                 source.GetInheritedProperty<bool> ("Nereid.SourceContentsPropagate"));
 
+            view_container.ClearHeaderWidget ();
             view_container.ClearFooter ();
             
             if (contents != null) {
@@ -379,6 +380,15 @@ namespace Nereid
             view_container.Header.Visible = source.Properties.Contains ("Nereid.SourceContents.HeaderVisible") ?
                 source.Properties.Get<bool> ("Nereid.SourceContents.HeaderVisible") : true;
 
+            Widget header_widget = null;
+            if (source.Properties.Contains ("Nereid.SourceContents.HeaderWidget")) {
+                header_widget = source.Properties.Get<Widget> ("Nereid.SourceContents.HeaderWidget");
+            }
+
+            if (header_widget != null) {
+                view_container.SetHeaderWidget (header_widget);
+            }
+
             Widget footer_widget = null;
             if (source.Properties.Contains ("Nereid.SourceContents.FooterWidget")) {
                 footer_widget = source.Properties.Get<Widget> ("Nereid.SourceContents.FooterWidget");
diff --git a/src/Clients/Nereid/Nereid/ViewContainer.cs b/src/Clients/Nereid/Nereid/ViewContainer.cs
index ef9c479..515e96a 100644
--- a/src/Clients/Nereid/Nereid/ViewContainer.cs
+++ b/src/Clients/Nereid/Nereid/ViewContainer.cs
@@ -46,6 +46,7 @@ namespace Nereid
     {
         private SearchEntry search_entry;
         private HBox header;
+        private EventBox header_box;
         private Label title_label;
         private Label search_label;
         private Banshee.ContextPane.ContextPane context_pane;
@@ -83,6 +84,8 @@ namespace Nereid
                     ServiceManager.Get<InterfaceActionService> ().SourceActions ["SourceContextMenuAction"].Activate ();
                 }
             };
+
+            header_box = new EventBox ();
             
             BuildSearchEntry ();
             
@@ -90,6 +93,7 @@ namespace Nereid
             search_label.MnemonicWidget = search_entry.InnerEntry;
             
             header.PackStart (title_box, true, true, 0);
+            header.PackStart (header_box, false, false, 0);
             header.PackStart (search_label, false, false, 5);
             header.PackStart (search_entry, false, false, 0);
             
@@ -185,7 +189,24 @@ namespace Nereid
                 editable.Position = search_entry.Query.Length;
             }
         }
-        
+
+        public void SetHeaderWidget (Widget widget)
+        {
+            if (widget != null) {
+                header_box.Add (widget);
+                widget.Show ();
+                header_box.Show ();
+            }
+        }
+
+        public void ClearHeaderWidget ()
+        {
+            header_box.Hide ();
+            if (header_box.Child != null) {
+                header_box.Remove (header_box.Child);
+            }
+        }
+
         public void SetFooter (Widget contents)
         {
             if (contents != null) {
diff --git a/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs b/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
index 6a4c340..f225a59 100644
--- a/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
+++ b/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
@@ -477,6 +477,12 @@ namespace Banshee.Collection
             get { return can_play; }
             set { can_play = value; }
         }
+
+        private bool enabled = true;
+        public bool Enabled {
+            get { return enabled && can_play; }
+            set { enabled = value; }
+        }
         
         public virtual string MetadataHash {
             get {
diff --git a/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs b/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs
index 41fad67..704dfff 100644
--- a/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs
+++ b/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs
@@ -35,11 +35,19 @@ namespace Banshee.Configuration
         public static SchemaEntry<T> Zero;
     
         public SchemaEntry (string @namespace, string key, T defaultValue, 
+            string shortDescription, string longDescription) :
+            this (@namespace, key, defaultValue, default(T), default(T), shortDescription, longDescription)
+        {
+        }
+
+        public SchemaEntry (string @namespace, string key, T defaultValue, T minValue, T maxValue,
             string shortDescription, string longDescription)
         {
             Namespace = @namespace;
             Key = key;
             DefaultValue = defaultValue;
+            MinValue = minValue;
+            MaxValue = maxValue;
             ShortDescription = shortDescription;
             LongDescription = longDescription;
         }
@@ -54,14 +62,24 @@ namespace Banshee.Configuration
             return ConfigurationClient.Get<T> (this, fallback);
         }
 
-        public void Set (T value)
+        public bool Set (T value)
         {
+            if (!Object.Equals (MinValue, default (T)) || !Object.Equals(MaxValue, default (T))) {
+                if (((IComparable<T>)MinValue).CompareTo (value) > 0 ||
+                    ((IComparable<T>)MaxValue).CompareTo (value) < 0) {
+                    return false;
+                }
+            }
+
             ConfigurationClient.Set<T> (this, value);
+            return true;
         }
 
         public readonly string Namespace;
         public readonly string Key;
         public readonly T DefaultValue;
+        public readonly T MinValue;
+        public readonly T MaxValue;
         public readonly string ShortDescription;
         public readonly string LongDescription;
         
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
index 54b239e..384edaf 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
@@ -187,7 +187,7 @@ namespace Banshee.Collection.Database
         }
 
         private string unfiltered_query;
-        public string UnfilteredQuery {
+        public virtual string UnfilteredQuery {
             get {
                 return unfiltered_query ?? (unfiltered_query = String.Format (
                     "FROM {0} WHERE {1} {2}",
diff --git a/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs b/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
index 51970ea..780f4b6 100644
--- a/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
+++ b/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
@@ -56,7 +56,7 @@ namespace Banshee.Database
         // NOTE: Whenever there is a change in ANY of the database schema,
         //       this version MUST be incremented and a migration method
         //       MUST be supplied to match the new version number
-        protected const int CURRENT_VERSION = 34;
+        protected const int CURRENT_VERSION = 35;
         protected const int CURRENT_METADATA_VERSION = 6;
         
 #region Migration Driver
@@ -789,6 +789,19 @@ namespace Banshee.Database
 
 #endregion
 
+#region Version 35
+
+        [DatabaseVersion (35)]
+        private bool Migrate_35 ()
+        {
+            if (!connection.ColumnExists ("CorePlaylistEntries", "Generated")) {
+                Execute ("ALTER TABLE CorePlaylistEntries ADD COLUMN Generated INTEGER NOT NULL DEFAULT 0");
+            }
+            return true;
+        }
+
+#endregion
+
 #pragma warning restore 0169
         
 #region Fresh database setup
@@ -950,7 +963,8 @@ namespace Banshee.Database
                     EntryID             INTEGER PRIMARY KEY,
                     PlaylistID          INTEGER NOT NULL,
                     TrackID             INTEGER NOT NULL,
-                    ViewOrder           INTEGER NOT NULL DEFAULT 0
+                    ViewOrder           INTEGER NOT NULL DEFAULT 0,
+                    Generated           INTEGER NOT NULL DEFAULT 0
                 )
             ");
             Execute("CREATE INDEX CorePlaylistEntriesIndex ON CorePlaylistEntries(PlaylistID, TrackID)");
diff --git a/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs b/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
index 97b3902..ee97408 100644
--- a/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
+++ b/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
@@ -258,7 +258,7 @@ namespace Banshee.Playlist
             return false;
         }
         
-        public void ReorderSelectedTracks (int drop_row)
+        public virtual void ReorderSelectedTracks (int drop_row)
         {
             if (TrackModel.Selection.Count == 0 || TrackModel.Selection.AllSelected) {
                 return;
diff --git a/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs b/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs
index defff60..416510f 100644
--- a/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs
+++ b/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs
@@ -58,12 +58,22 @@ namespace Banshee.Preferences
         public override T Value {
             get { return schema.Get (); }
             set { 
-                schema.Set (value); 
+                if (!schema.Set (value)) {
+                    return;
+                }
                 if (handler != null) {
                     handler ();
                 }
                 OnValueChanged ();
             }
         }
+
+        public T MinValue {
+            get { return schema.MinValue; }
+        }
+
+        public T MaxValue {
+            get { return schema.MaxValue; }
+        }
     }
 }
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs
index 0d21bc4..fb52f7e 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs
@@ -49,7 +49,7 @@ namespace Banshee.Collection.Gui
         public BaseTrackListView () : base ()
         {
             RulesHint = true;
-            RowSensitivePropertyName = "CanPlay";
+            RowOpaquePropertyName = "Enabled";
             RowBoldPropertyName = "IsPlaying";
             
             ServiceManager.PlayerEngine.ConnectEvent (
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs
index 6912b74..170e473 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs
@@ -86,14 +86,14 @@ namespace Banshee.Collection.Gui
                     Cairo.Rectangle pixbuf_area = new Cairo.Rectangle (draw_x,
                         (cellHeight - render_pixbuf.Height) / 2, render_pixbuf.Width, render_pixbuf.Height);
                     
-                    if (!context.Sensitive) {
+                    if (!context.Opaque) {
                         context.Context.Save ();
                     }
                     
                     Gdk.CairoHelper.SetSourcePixbuf (context.Context, render_pixbuf, pixbuf_area.X, pixbuf_area.Y);
                     context.Context.Rectangle (pixbuf_area);
                     
-                    if (!context.Sensitive) {
+                    if (!context.Opaque) {
                         context.Context.Clip ();
                         context.Context.PaintWithAlpha (0.5);
                         context.Context.Restore ();
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs
index 06d82d9..134c1f1 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs
@@ -142,14 +142,14 @@ namespace Banshee.Collection.Gui
             Cairo.Rectangle pixbuf_area = new Cairo.Rectangle ((cellWidth - render_pixbuf.Width) / 2, 
                 (cellHeight - render_pixbuf.Height) / 2, render_pixbuf.Width, render_pixbuf.Height);
             
-            if (!context.Sensitive) {
+            if (!context.Opaque) {
                 context.Context.Save ();
             }
             
             Gdk.CairoHelper.SetSourcePixbuf (context.Context, render_pixbuf, pixbuf_area.X, pixbuf_area.Y);
             context.Context.Rectangle (pixbuf_area);
             
-            if (!context.Sensitive) {
+            if (!context.Opaque) {
                 context.Context.Clip ();
                 context.Context.PaintWithAlpha (0.5);
                 context.Context.Restore ();
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs
index 226f55f..31783cf 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs
@@ -81,7 +81,7 @@ namespace Banshee.Collection.Gui
             context.Context.MoveTo (4, ((int)cellHeight - text_height) / 2);
             Cairo.Color color = context.Theme.Colors.GetWidgetColor (
                 context.TextAsForeground ? GtkColorClass.Foreground : GtkColorClass.Text, state);
-            color.A = (!context.Sensitive) ? 0.3 : 1.0;
+            color.A = (!context.Opaque) ? 0.3 : 1.0;
             context.Context.Color = color;
             
             PangoCairoHelper.ShowLayout (context.Context, context.Layout);
diff --git a/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs b/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs
index 969fcda..bec410d 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs
@@ -56,6 +56,13 @@ namespace Banshee.Preferences.Gui
                 pref_widget = new PreferenceCheckButton (preference);
             } else if (type == typeof (string)) {
                 pref_widget = new PreferenceEntry (preference);
+            } else if (type == typeof (int)) {
+                var schema_preference = preference as SchemaPreference<int>;
+                if (schema_preference == null) {
+                    pref_widget = new PreferenceSpinButton (preference);
+                } else {
+                    pref_widget = new PreferenceSpinButton (preference, schema_preference.MinValue, schema_preference.MaxValue);
+                }
             }
 
             if (pref_widget != null) {
@@ -146,5 +153,30 @@ namespace Banshee.Preferences.Gui
                 }
             }
         }
+
+        private class PreferenceSpinButton : HBox
+        {
+            private bool sync;
+            private PreferenceBase preference;
+
+            public PreferenceSpinButton (PreferenceBase preference) : this (preference, 0, 100)
+            {
+            }
+
+            public PreferenceSpinButton (PreferenceBase preference, int min_value, int max_value)
+            {
+                var spin_button = new SpinButton (min_value, max_value, 1);
+                spin_button.Changed += delegate {
+                    if (sync) {
+                        this.preference.BoxedValue = (int)spin_button.Value;
+                    }
+                };
+                spin_button.Show ();
+                PackEnd (spin_button, false, false, 0);
+                this.preference = preference;
+                spin_button.Value = (int)preference.BoxedValue;
+                sync = true;
+            }
+        }
     }
 }
diff --git a/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs b/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs
index 51e62ca..f53d07e 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs
@@ -146,6 +146,18 @@ namespace Banshee.Sources.Gui
             bool hide_counts = source.Count <= 0;
             
             Pixbuf icon = SourceIconResolver.ResolveIcon (source);
+
+            if (state == StateType.Insensitive) {
+                // Code ported from gtk_cell_renderer_pixbuf_render()
+                var icon_source = new IconSource () {
+                    Pixbuf = icon,
+                    Size = IconSize.SmallToolbar,
+                    SizeWildcarded = false
+                };
+
+                icon = widget.Style.RenderIcon (icon_source, widget.Direction, state,
+                    (IconSize)(-1), widget, "SourceRowRenderer");
+            }
             
             FontDescription fd = widget.PangoContext.FontDescription.Copy ();
             fd.Weight = (ISource)ServiceManager.PlaybackController.NextSource == (ISource)source 
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml
index 014a6cb..05ddffb 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml
@@ -7,7 +7,7 @@
     name="Play Queue"
     category="User Interface"
     description="Provides a source that acts as a queue of playing tracks. When the queue is populated, all playback happens from the queue in a forced order."
-    author="Aaron Bockover"
+    author="Aaron Bockover, Alexander Kojevnikov"
     url="http://banshee-project.org/";
     defaultEnabled="true">
 
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
index 5fe9ce6..d3cb3dd 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
@@ -72,6 +72,9 @@
     <Compile Include="Banshee.PlayQueue\PlayQueueSource.cs" />
     <Compile Include="Banshee.PlayQueue\PlayQueueActions.cs" />
     <Compile Include="Banshee.PlayQueue\IPlayQueue.cs" />
+    <Compile Include="Banshee.PlayQueue\PlayQueueTrackListModel.cs" />
+    <Compile Include="Banshee.PlayQueue\QueueableSourceComboBox.cs" />
+    <Compile Include="Banshee.PlayQueue\HeaderWidget.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Resources\ActiveSourceUI.xml">
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs
new file mode 100644
index 0000000..96e6e63
--- /dev/null
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs
@@ -0,0 +1,128 @@
+//
+// HeaderWidget.cs
+//
+// Author:
+//   Alexander Kojevnikov <alexander kojevnikov com>
+//
+// Copyright (C) 2009 Alexander Kojevnikov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using Gtk;
+using Mono.Unix;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Banshee.PlaybackController;
+using Banshee.Sources;
+
+namespace Banshee.PlayQueue
+{
+    public class HeaderWidget : HBox
+    {
+        public event EventHandler<ModeChangedEventArgs> ModeChanged;
+        public event EventHandler<SourceChangedEventArgs> SourceChanged;
+
+        private readonly List<Widget> sensitive_widgets = new List<Widget> ();
+        private readonly Dictionary<string, PlaybackShuffleMode> modes = new Dictionary<string, PlaybackShuffleMode> {
+            { Catalog.GetString ("manually"), PlaybackShuffleMode.Linear },
+            { Catalog.GetString ("by song"), PlaybackShuffleMode.Song },
+            { Catalog.GetString ("by album"), PlaybackShuffleMode.Album },
+            { Catalog.GetString ("by artist"), PlaybackShuffleMode.Artist },
+            { Catalog.GetString ("by rating"), PlaybackShuffleMode.Rating },
+            { Catalog.GetString ("by score"), PlaybackShuffleMode.Score },
+        };
+
+        public HeaderWidget (PlaybackShuffleMode mode, string source_name) : base ()
+        {
+            this.Spacing = 6;
+
+            var fill_label = new Label (Catalog.GetString ("_Fill"));
+            var mode_combo = new ComboBox (modes.Keys.OrderBy (s => modes[s]).ToArray ());
+            fill_label.MnemonicWidget = mode_combo;
+            mode_combo.Changed += delegate {
+                var value = modes[mode_combo.ActiveText];
+                foreach (var widget in sensitive_widgets) {
+                    widget.Sensitive = value != PlaybackShuffleMode.Linear;
+                }
+                var handler = ModeChanged;
+                if (handler != null) {
+                    handler (this, new ModeChangedEventArgs (value));
+                }
+            };
+
+            var from_label = new Label (Catalog.GetString ("f_rom"));
+            sensitive_widgets.Add (from_label);
+            var source_combo_box = new QueueableSourceComboBox (source_name);
+            from_label.MnemonicWidget = source_combo_box;
+            sensitive_widgets.Add (source_combo_box);
+            source_combo_box.Changed += delegate {
+                var handler = SourceChanged;
+                if (handler != null) {
+                    handler (this, new SourceChangedEventArgs (source_combo_box.Source));
+                }
+            };
+
+            PackStart (fill_label, false, false, 0);
+            PackStart (mode_combo, false, false, 0);
+            PackStart (from_label, false, false, 0);
+            PackStart (source_combo_box, false, false, 0);
+
+            // Select the population mode.
+            mode_combo.Model.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter) {
+                if (modes[(string)model.GetValue (iter, 0)] == mode) {
+                    mode_combo.SetActiveIter (iter);
+                    return true;
+                }
+                return false;
+            });
+        }
+    }
+
+    public sealed class ModeChangedEventArgs : EventArgs
+    {
+        private PlaybackShuffleMode value;
+
+        public ModeChangedEventArgs (PlaybackShuffleMode value)
+        {
+            this.value = value;
+        }
+
+        public PlaybackShuffleMode Value {
+            get { return this.value; }
+        }
+    }
+
+    public sealed class SourceChangedEventArgs : EventArgs
+    {
+        private ITrackModelSource value;
+
+        public SourceChangedEventArgs (ITrackModelSource value)
+        {
+            this.value = value;
+        }
+
+        public ITrackModelSource Value {
+            get { return this.value; }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs
index 0b655c1..8c6318d 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs
@@ -52,6 +52,13 @@ namespace Banshee.PlayQueue
             });
             
             AddImportant (
+                new ActionEntry ("RefreshPlayQueueAction", Stock.Refresh,
+                    Catalog.GetString ("Refresh"), null,
+                    Catalog.GetString ("Refresh random tracks in the play queue"),
+                    OnRefreshPlayQueue)
+            );
+
+            AddImportant (
                 new ActionEntry ("ClearPlayQueueAction", Stock.Clear,
                     Catalog.GetString ("Clear"), null,
                     Catalog.GetString ("Remove all tracks from the play queue"),
@@ -94,6 +101,11 @@ namespace Banshee.PlayQueue
             playqueue.Clear ();
         }
 
+        private void OnRefreshPlayQueue (object o, EventArgs args)
+        {
+            playqueue.Refresh ();
+        }
+
         private void OnClearPlayQueueOnQuit (object o, EventArgs args)
         {
             ToggleAction action = this["ClearPlayQueueOnQuitAction"] as Gtk.ToggleAction;
@@ -117,7 +129,9 @@ namespace Banshee.PlayQueue
             Source source = ServiceManager.SourceManager.ActiveSource;
             if (source != null) {
                 DatabaseSource db_source = source as DatabaseSource ?? source.Parent as DatabaseSource;
-                UpdateAction ("ClearPlayQueueAction", true, playqueue.Count > 0);
+                UpdateAction ("RefreshPlayQueueAction", playqueue.Populate);
+                UpdateAction ("ClearPlayQueueAction", !playqueue.Populate, playqueue.Count > 0);
+                UpdateAction ("ClearPlayQueueOnQuitAction", !playqueue.Populate);
                 UpdateAction ("AddToPlayQueueAction", db_source != null && db_source != playqueue, true);
             }
         }
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
index 76957b7..217a9b0 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
@@ -1,10 +1,12 @@
 //
 // PlayQueueSource.cs
 //
-// Author:
+// Authors:
 //   Aaron Bockover <abockover novell com>
+//   Alexander Kojevnikov <alexander kojevnikov com>
 //
 // Copyright (C) 2008 Novell, Inc.
+// Copyright (C) 2009 Alexander Kojevnikov
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -27,38 +29,50 @@
 //
 
 using System;
+using System.Collections.Generic;
+using System.Linq;
 
 using Mono.Unix;
 
+using Hyena.Collections;
 using Hyena.Data.Sqlite;
 
-using Banshee.ServiceStack;
-using Banshee.Sources;
-using Banshee.Playlist;
-using Banshee.Library;
-using Banshee.Database;
 using Banshee.Collection;
 using Banshee.Collection.Database;
-using Banshee.PlaybackController;
-using Banshee.MediaEngine;
 using Banshee.Configuration;
+using Banshee.Database;
 using Banshee.Gui;
+using Banshee.Library;
+using Banshee.MediaEngine;
+using Banshee.PlaybackController;
+using Banshee.Playlist;
+using Banshee.Preferences;
+using Banshee.ServiceStack;
+using Banshee.Sources;
 
 namespace Banshee.PlayQueue
 {
     public class PlayQueueSource : PlaylistSource, IBasicPlaybackController, IPlayQueue, IDBusExportable, IDisposable
     {
-        private static string special_playlist_name = "Play Queue";//typeof (PlayQueueSource).ToString ();
+        private static string special_playlist_name = "Play Queue";
 
         private ITrackModelSource prior_playback_source;
-        private DatabaseTrackInfo playing_track;
+        private DatabaseTrackInfo current_track;
+        private long offset;
         private TrackInfo prior_playback_track;
         private PlayQueueActions actions;
         private bool was_playing = false;
-        
-        protected override bool HasArtistAlbum {
-            get { return false; }
-        }
+        protected DateTime source_set_at = DateTime.MinValue;
+        private HeaderWidget header_widget;
+
+        private SourcePage pref_page;
+        private Section pref_section;
+
+        private PlaybackShuffleMode populate_mode = (PlaybackShuffleMode) PopulateModeSchema.Get ();
+        private string populate_from_name = PopulateFromSchema.Get ();
+        private ITrackModelSource populate_from = null;
+        private int played_songs_number = PlayedSongsNumberSchema.Get ();
+        private int upcoming_songs_number = UpcomingSongsNumberSchema.Get ();
         
         public PlayQueueSource () : base (Catalog.GetString ("Play Queue"), null)
         {
@@ -70,12 +84,12 @@ namespace Banshee.PlayQueue
             Order = 20;
             Properties.SetString ("Icon.Name", "source-playlist");
             Properties.SetString ("RemoveTracksActionLabel", Catalog.GetString ("Remove From Play Queue"));
-            
+
             DatabaseTrackModel.ForcedSortQuery = "CorePlaylistEntries.ViewOrder ASC, CorePlaylistEntries.EntryID ASC";
             DatabaseTrackModel.CanReorder = true;
-            
+
             ServiceManager.PlayerEngine.ConnectEvent (OnPlayerEvent);
-            ServiceManager.PlaybackController.Transition += OnCanonicalPlaybackControllerTransition;
+            ServiceManager.PlaybackController.TrackStarted += OnTrackStarted;
 
             ServiceManager.SourceManager.AddSource (this);
             
@@ -92,18 +106,43 @@ namespace Banshee.PlayQueue
             ServiceManager.SourceManager.VideoLibrary.TracksChanged += HandleTracksChanged;
             ServiceManager.SourceManager.VideoLibrary.TracksDeleted += HandleTracksDeleted;
             
-            TrackModel.Reloaded += delegate {
-                if (Count == 0) {
-                    if (this == ServiceManager.PlaybackController.Source || this == ServiceManager.PlaybackController.NextSource) {
-                        ServiceManager.PlaybackController.NextSource = ServiceManager.PlaybackController.Source = PriorSource;
-                    }
-                }
+            populate_from = ServiceManager.SourceManager.Sources.FirstOrDefault (
+                source => source.Name == populate_from_name) as ITrackModelSource;
+            if (populate_from != null) {
+                populate_from.Reload ();
+            }
+
+            TrackModel.Reloaded += HandleReloaded;
+
+            Offset = CurrentOffsetSchema.Get ();
+        }
+
+        protected override void Initialize ()
+        {
+            base.Initialize ();
+
+            InstallPreferences ();
+
+            header_widget = new HeaderWidget (populate_mode, populate_from_name);
+            header_widget.ModeChanged += delegate(object sender, ModeChangedEventArgs e) {
+                populate_mode = e.Value;
+                PopulateModeSchema.Set ((int) e.Value);
+                UpdatePlayQueue ();
+                OnUpdated ();
             };
-            
-            Reload ();
-            SetAsPlaybackSourceUnlessPlaying ();
+            header_widget.SourceChanged += delegate(object sender, SourceChangedEventArgs e) {
+                populate_from = e.Value;
+                populate_from_name = e.Value.Name;
+                PopulateFromSchema.Set (e.Value.Name);
+                source_set_at = DateTime.Now;
+                populate_from.Reload ();
+                Refresh ();
+            };
+            header_widget.ShowAll ();
+
+            Properties.Set<Gtk.Widget> ("Nereid.SourceContents.HeaderWidget", header_widget);
         }
-        
+
 #region IPlayQueue, IDBusExportable
 
         public void EnqueueUri (string uri)
@@ -113,33 +152,66 @@ namespace Banshee.PlayQueue
 
         public void EnqueueUri (string uri, bool prepend)
         {
-            EnqueueId (DatabaseTrackInfo.GetTrackIdForUri (uri), prepend);
+            EnqueueId (DatabaseTrackInfo.GetTrackIdForUri (uri), prepend, false);
         }
         
         public void EnqueueTrack (TrackInfo track, bool prepend)
         {
             DatabaseTrackInfo db_track = track as DatabaseTrackInfo;
             if (db_track != null) {
-                EnqueueId (db_track.TrackId, prepend);
+                EnqueueId (db_track.TrackId, prepend, false);
             } else {
                 EnqueueUri (track.Uri.AbsoluteUri, prepend);
             }
         }
         
-        private void EnqueueId (int trackId, bool prepend)
+        private void EnqueueId (int trackId, bool prepend, bool generated)
         {
             if (trackId <= 0) {
                 return;
             }
-            
-            if (prepend) {
-                ServiceManager.DbConnection.Execute ("UPDATE CorePlaylistEntries SET ViewOrder = ViewOrder + 1 WHERE PlaylistID = ?", DbId);
+
+            long view_order;
+            if (prepend && current_track != null) {
+                // We are going to prepend the track to the play queue, which means
+                // adding it after the current_track. Now find the corresponding view_order.
+                view_order = ServiceManager.DbConnection.Query<long> (@"
+                    SELECT ViewOrder + 1
+                    FROM CorePlaylistEntries
+                    WHERE PlaylistID = ? AND EntryID = ?",
+                    DbId, Convert.ToInt64 (current_track.CacheEntryId)
+                );
+            } else {
+                if (generated) {
+                    // view_order will point after the last track in the queue.
+                    view_order = MaxViewOrder;
+                }
+                else {
+                    // view_order will point after the last non-generated track in the queue.
+                    view_order = ServiceManager.DbConnection.Query<long> (@"
+                        SELECT MAX(ViewOrder) + 1
+                        FROM CorePlaylistEntries
+                        WHERE PlaylistID = ? AND Generated = 0",
+                        DbId
+                    );
+                }
             }
 
-            HyenaSqliteCommand insert_command = new HyenaSqliteCommand (String.Format (
-                @"INSERT INTO CorePlaylistEntries (PlaylistID, TrackID, ViewOrder) VALUES ({0}, ?, {1})", 
-                    DbId, prepend ? 0 : MaxViewOrder));
-            ServiceManager.DbConnection.Execute (insert_command, trackId);
+            // Increment the order of all tracks after view_order
+            ServiceManager.DbConnection.Execute (@"
+                UPDATE CorePlaylistEntries
+                SET ViewOrder = ViewOrder + 1
+                WHERE PlaylistID = ? AND ViewOrder >= ?",
+                DbId, view_order
+            );
+
+            // Add the track to the queue using the view order calculated above.
+            ServiceManager.DbConnection.Execute (@"
+                INSERT INTO CorePlaylistEntries
+                (PlaylistID, TrackID, ViewOrder, Generated)
+                VALUES (?, ?, ?, ?)",
+                DbId, trackId, view_order, generated ? 1 : 0
+            );
 
             OnTracksAdded ();
             NotifyUser ();
@@ -154,10 +226,74 @@ namespace Banshee.PlayQueue
         }
 
 #endregion
-        
+
+        public override bool AddSelectedTracks (Source source)
+        {
+            if ((Parent == null || source == Parent || source.Parent == Parent) && AcceptsInputFromSource (source)) {
+                DatabaseTrackListModel model = (source as ITrackModelSource).TrackModel as DatabaseTrackListModel;
+                if (model == null) {
+                    return false;
+                }
+
+                // Get the ViewOrder of the current_track
+                long current_view_order = current_track == null ?
+                    ServiceManager.DbConnection.Query<long> (@"
+                        SELECT MAX(ViewOrder) + 1
+                        FROM CorePlaylistEntries
+                        WHERE PlaylistID = ?",
+                        DbId
+                    ) :
+                    ServiceManager.DbConnection.Query<long> (@"
+                        SELECT ViewOrder
+                        FROM CorePlaylistEntries
+                        WHERE PlaylistID = ? AND EntryID = ?",
+                        DbId, Convert.ToInt64 (current_track.CacheEntryId)
+                );
+
+                // If the current_track is not playing, insert before it.
+                int index = -1;
+                if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+                    current_view_order--;
+                    index = TrackModel.IndexOf (current_track);
+                }
+
+                // view_order will point to the last pending non-generated track in the queue
+                // or to the current_track if all tracks are generated. We want to insert tracks after it.
+                long view_order = Math.Max(current_view_order, ServiceManager.DbConnection.Query<long> (@"
+                    SELECT MAX(ViewOrder)
+                    FROM CorePlaylistEntries
+                    WHERE PlaylistID = ? AND ViewOrder > ? AND Generated = 0",
+                    DbId, current_view_order
+                ));
+
+                // Add the tracks to the end of the queue.
+                WithTrackSelection (model, AddTrackRange);
+
+                // Shift generated tracks to the end of the queue.
+                ServiceManager.DbConnection.Execute (@"
+                    UPDATE CorePlaylistEntries
+                    SET ViewOrder = ViewOrder - ? + ?
+                    WHERE PlaylistID = ? AND ViewOrder > ? AND Generated = 1",
+                    view_order, MaxViewOrder, DbId, view_order
+                );
+
+                OnTracksAdded ();
+                OnUserNotifyUpdated ();
+
+                // If the current_track was not playing, and there were no non-generated tracks,
+                // mark the first added track as current.
+                if (view_order == current_view_order && current_track != null &&
+                    !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+                    SetCurrentTrack (TrackModel[index] as DatabaseTrackInfo);
+                }
+                return true;
+            }
+            return false;
+        }
+
         private void SetAsPlaybackSourceUnlessPlaying ()
         {
-            if (Count > 0 && ServiceManager.PlaybackController.Source != this) {
+            if (current_track != null && ServiceManager.PlaybackController.Source != this) {
                 PriorSource = ServiceManager.PlaybackController.Source;
                 ServiceManager.PlaybackController.NextSource = this;
             }
@@ -165,20 +301,42 @@ namespace Banshee.PlayQueue
 
         public void Clear ()
         {
-            playing_track = null;
-            RemoveTrackRange (DatabaseTrackModel, new Hyena.Collections.RangeCollection.Range (0, Count));
+            ServiceManager.DbConnection.Execute (@"
+                DELETE FROM CorePlaylistEntries
+                WHERE PlaylistID = ?", DbId
+            );
+            offset = 0;
+            SetCurrentTrack (null);
+
+            if (this == ServiceManager.PlaybackController.Source && ServiceManager.PlayerEngine.IsPlaying ()) {
+                ServiceManager.PlayerEngine.Close();
+            }
+
             Reload ();
         }
 
         public void Dispose ()
         {
+            int track_index = current_track == null ? Count : Math.Max (0, TrackModel.IndexOf (current_track));
+            CurrentTrackSchema.Set (track_index);
+
             ServiceManager.PlayerEngine.DisconnectEvent (OnPlayerEvent);
+            ServiceManager.PlaybackController.TrackStarted -= OnTrackStarted;
 
             if (actions != null) {
                 actions.Dispose ();
             }
 
-            if (ClearOnQuitSchema.Get ()) {
+            UninstallPreferences ();
+
+            Properties.Remove ("Nereid.SourceContents.HeaderWidget");
+
+            if (header_widget != null) {
+                header_widget.Destroy ();
+                header_widget = null;
+            }
+
+            if (!Populate && ClearOnQuitSchema.Get ()) {
                 Clear ();
             }
         }
@@ -201,22 +359,84 @@ namespace Banshee.PlayQueue
 
         protected override void OnTracksAdded ()
         {
+            int old_count = Count;
+
             base.OnTracksAdded ();
+
+            if (current_track == null && old_count < Count) {
+                SetCurrentTrack (TrackModel[old_count] as DatabaseTrackInfo);
+            }
+
             SetAsPlaybackSourceUnlessPlaying ();
         }
-        
-        private void OnCanonicalPlaybackControllerTransition (object o, EventArgs args)
+
+        protected override void OnTracksRemoved ()
         {
-            if (Count > 0) {
-                PriorSource = ServiceManager.PlaybackController.Source;
-                ServiceManager.PlaybackController.Source = this;
+            base.OnTracksRemoved ();
+
+            if (this == ServiceManager.PlaybackController.Source &&
+                ServiceManager.PlayerEngine.IsPlaying () &&
+                TrackModel.IndexOf (ServiceManager.PlayerEngine.CurrentTrack) == -1) {
+                if (ServiceManager.PlayerEngine.CurrentState == PlayerState.Paused || current_track == null) {
+                    ServiceManager.PlayerEngine.Close();
+                } else {
+                    ServiceManager.PlayerEngine.OpenPlay (current_track);
+                }
             }
+            UpdatePlayQueue ();
         }
-        
+
+        protected override void RemoveTrackRange (DatabaseTrackListModel model, RangeCollection.Range range)
+        {
+            base.RemoveTrackRange (model, range);
+
+            model.Selection.UnselectRange (range.Start, range.End);
+
+            int index = TrackModel.IndexOf (current_track);
+            if (range.Start <= index && index <= range.End) {
+                SetCurrentTrack (range.End + 1 < Count ? TrackModel[range.End + 1] as DatabaseTrackInfo : null);
+            }
+        }
+
+        private void HandleReloaded(object sender, EventArgs e)
+        {
+            int track_index = CurrentTrackSchema.Get ();
+            if (track_index < Count) {
+                SetCurrentTrack (TrackModel[track_index] as DatabaseTrackInfo);
+            }
+
+            SetAsPlaybackSourceUnlessPlaying ();
+
+            TrackModel.Reloaded -= HandleReloaded;
+        }
+
+        public override void ReorderSelectedTracks (int drop_row)
+        {
+            // If the current_track is not playing, make the first pending unselected track the current one.
+            if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+                int current_index = TrackModel.IndexOf (current_track);
+                int new_index = -1;
+                for (int index = current_index; index < TrackModel.Count; index++) {
+                    if (!TrackModel.Selection.Contains (index)) {
+                        new_index = index;
+                        break;
+                    }
+                }
+                if (new_index != current_index) {
+                    SetCurrentTrack (new_index == -1 ? null : TrackModel[new_index] as DatabaseTrackInfo);
+                }
+            }
+
+            base.ReorderSelectedTracks (drop_row);
+        }
+
         private void OnPlayerEvent (PlayerEventArgs args)
         {
             if (args.Event == PlayerEvent.EndOfStream) {
-                if (RemovePlayingTrack () && Count == 0) {
+                if (this == ServiceManager.PlaybackController.Source &&
+                    TrackModel.IndexOf (current_track) == Count - 1) {
+                    SetCurrentTrack (null);
+                    UpdatePlayQueue ();
                     if (was_playing) {
                         ServiceManager.PlaybackController.PriorTrack = prior_playback_track;
                     } else {
@@ -224,15 +444,34 @@ namespace Banshee.PlayQueue
                     }
                 }
             } else if (args.Event == PlayerEvent.StartOfStream) {
-                if (this == ServiceManager.PlaybackController.Source) {
-                    playing_track = ServiceManager.PlayerEngine.CurrentTrack as DatabaseTrackInfo;
+                if (TrackModel.IndexOf (ServiceManager.PlayerEngine.CurrentTrack) != -1) {
+                    SetCurrentTrack (ServiceManager.PlayerEngine.CurrentTrack as DatabaseTrackInfo);
+                    SetAsPlaybackSourceUnlessPlaying ();
+                    UpdatePlayQueue ();
                 } else {
-                    playing_track = null;
                     prior_playback_track = ServiceManager.PlayerEngine.CurrentTrack;
                 }
             }
         }
 
+        public override void Reload ()
+        {
+            enabled_cache.Clear ();
+            base.Reload ();
+
+            if (current_track == null) {
+                if (this == ServiceManager.PlaybackController.Source ||
+                    this == ServiceManager.PlaybackController.NextSource) {
+                    ServiceManager.PlaybackController.NextSource = PriorSource;
+                }
+            }
+        }
+
+        protected override DatabaseTrackListModel CreateTrackModelFor (DatabaseSource src)
+        {
+            return new PlayQueueTrackListModel (ServiceManager.DbConnection, DatabaseTrackInfo.Provider, (PlayQueueSource) src);
+        }
+
         bool IBasicPlaybackController.First ()
         {
             return ((IBasicPlaybackController)this).Next (false);
@@ -240,9 +479,12 @@ namespace Banshee.PlayQueue
         
         bool IBasicPlaybackController.Next (bool restart)
         {
-            RemovePlayingTrack ();
-            
-            if (Count == 0) {
+            if (current_track != null && ServiceManager.PlayerEngine.CurrentTrack == current_track) {
+                int index = TrackModel.IndexOf (current_track) + 1;
+                SetCurrentTrack (index < Count ? TrackModel[index] as DatabaseTrackInfo : null);
+            }
+            if (current_track == null) {
+                UpdatePlayQueue ();
                 ServiceManager.PlaybackController.Source = PriorSource;
                 if (was_playing) {
                     ServiceManager.PlaybackController.PriorTrack = prior_playback_track;
@@ -252,26 +494,185 @@ namespace Banshee.PlayQueue
                 }
                 return true;
             }
-            
-            ServiceManager.PlayerEngine.OpenPlay ((DatabaseTrackInfo)TrackModel[0]);
+
+            ServiceManager.PlayerEngine.OpenPlay (current_track);
             return true;
         }
         
         bool IBasicPlaybackController.Previous (bool restart)
         {
+            if (current_track != null && ServiceManager.PlayerEngine.CurrentTrack == current_track) {
+                int index = TrackModel.IndexOf (current_track);
+                if (index > 0) {
+                    SetCurrentTrack (TrackModel[index - 1] as DatabaseTrackInfo);
+                }
+                ServiceManager.PlayerEngine.OpenPlay (current_track);
+            }
             return true;
         }
         
-        private bool RemovePlayingTrack ()
+        private void UpdatePlayQueue ()
         {
-            if (playing_track != null) {
-                RemoveTrack (playing_track);
-                playing_track = null;
-                return true;
+            // Find the ViewOrder of the current_track.
+            long view_order;
+            if (current_track == null) {
+                view_order = ServiceManager.DbConnection.Query<long> (@"
+                    SELECT MAX(ViewOrder) + 1
+                    FROM CorePlaylistEntries
+                    WHERE PlaylistID = ?", DbId
+                );
+            }
+            else {
+                view_order = ServiceManager.DbConnection.Query<long> (@"
+                    SELECT ViewOrder
+                    FROM CorePlaylistEntries
+                    WHERE PlaylistID = ? AND EntryID = ?",
+                    DbId, Convert.ToInt64 (current_track.CacheEntryId)
+                );
+            }
+
+            // Offset the model so that no more than played_songs_number tracks are shown before the current_track.
+            Offset = played_songs_number == 0 ? view_order : ServiceManager.DbConnection.Query<long> (@"
+                SELECT MIN(ViewOrder)
+                FROM (
+                    SELECT ViewOrder
+                    FROM CorePlaylistEntries
+                    WHERE PlaylistID = ? AND ViewOrder < ?
+                    ORDER BY ViewOrder DESC
+                    LIMIT ?
+                )", DbId, view_order, played_songs_number
+            );
+
+            // Check if we need to add more tracks.
+            int tracks_to_add = upcoming_songs_number -
+                (current_track == null ? 0 : Count - TrackModel.IndexOf (current_track) - 1);
+
+            // If the current track is not playing count it as well.
+            if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+                tracks_to_add--;
+            }
+
+            if (tracks_to_add > 0 && Populate && populate_from != null) {
+                // Add songs from the selected source, skip if all tracks need to be populated.
+                bool skip = tracks_to_add == upcoming_songs_number;
+                for (int i = 0; i < tracks_to_add; i++) {
+                    var track = populate_from.TrackModel.GetRandom (
+                        source_set_at, populate_mode, false, skip && i == 0) as DatabaseTrackInfo;
+                    if (track != null) {
+                        track.LastPlayed = DateTime.Now;
+                        // track.Save() is quite slow, update LastPlayedStamp directly in the database.
+                        ServiceManager.DbConnection.Execute (@"
+                            UPDATE CoreTracks
+                            SET LastPlayedStamp = ?
+                            WHERE TrackID = ?",
+                            Hyena.DateTimeUtil.ToTimeT (track.LastPlayed), track.TrackId
+                        );
+                        EnqueueId (track.TrackId, false, true);
+                    }
+                }
+                OnTracksAdded ();
+                if (current_track == null && Count > 0) {
+                    // If the queue was empty, make the first added track the current one.
+                    SetCurrentTrack (TrackModel[0] as DatabaseTrackInfo);
+                    ServiceManager.PlayerEngine.OpenPlay (current_track);
+                }
             }
-            return false;
         }
-        
+
+        private readonly Dictionary<int, bool> enabled_cache = new Dictionary<int, bool> ();
+        public bool IsTrackEnabled (int index)
+        {
+            if (!enabled_cache.ContainsKey (index)) {
+                int current_index = current_track == null ? Count : TrackModel.IndexOf (current_track);
+                enabled_cache.Add (index, index >= current_index);
+            }
+            return enabled_cache[index];
+        }
+
+        private void SetCurrentTrack (DatabaseTrackInfo track)
+        {
+            enabled_cache.Clear ();
+            current_track = track;
+        }
+
+        public long Offset {
+            get { return offset; }
+            protected set {
+                if (value != offset) {
+                    offset = value;
+                    CurrentOffsetSchema.Set ((int) offset);
+                    Reload ();
+                }
+            }
+        }
+
+        public void Refresh ()
+        {
+            int index = current_track == null ? Count : TrackModel.IndexOf (current_track);
+
+            // If the current track is not playing refresh it too.
+            if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+                index--;
+            }
+
+            if (index + 1 < Count) {
+                // Get the ViewOrder of the current_track
+                long current_view_order = current_track == null ?
+                    ServiceManager.DbConnection.Query<long> (@"
+                        SELECT MAX(ViewOrder) + 1
+                        FROM CorePlaylistEntries
+                        WHERE PlaylistID = ?",
+                        DbId
+                    ) :
+                    ServiceManager.DbConnection.Query<long> (@"
+                        SELECT ViewOrder
+                        FROM CorePlaylistEntries
+                        WHERE PlaylistID = ? AND EntryID = ?",
+                        DbId, Convert.ToInt64 (current_track.CacheEntryId)
+                );
+                // Get the list of generated tracks.
+                var generated = new HashSet<long> ();
+                foreach(long trackID in ServiceManager.DbConnection.QueryEnumerable<long> ( @"
+                    SELECT TrackID
+                    FROM CorePlaylistEntries
+                    WHERE PlaylistID = ? AND Generated = 1 AND ViewOrder >= ?",
+                    DbId, current_view_order)) {
+
+                    generated.Add (trackID);
+                }
+
+                // Collect the indices of all generated tracks.
+                var ranges = new RangeCollection ();
+                for (int i = index + 1; i < Count; i++) {
+                    if (generated.Contains (((DatabaseTrackInfo)TrackModel[i]).TrackId)) {
+                        ranges.Add (i);
+                    }
+                }
+
+                bool removed = false;
+                foreach (var range in ranges.Ranges) {
+                    RemoveTrackRange (DatabaseTrackModel, range);
+                    removed = true;
+                }
+
+                if (removed) {
+                    OnTracksRemoved ();
+                }
+            }
+            else if (Count == 0 || current_track == null) {
+                UpdatePlayQueue ();
+            }
+        }
+
+        private void OnTrackStarted(object sender, EventArgs e)
+        {
+            SetAsPlaybackSourceUnlessPlaying ();
+        }
+
+        public bool Populate {
+            get { return populate_mode != PlaybackShuffleMode.Linear; }
+        }
+
         private ITrackModelSource PriorSource {
             get {
                 if (prior_playback_source == null || prior_playback_source == this) {
@@ -287,15 +688,23 @@ namespace Banshee.PlayQueue
                 was_playing = ServiceManager.PlayerEngine.IsPlaying ();
             }
         }
-        
+
         public override bool CanRename {
             get { return false; }
         }
-        
+
+        public override bool CanSearch {
+            get { return false; }
+        }
+
         public override bool ShowBrowser {
             get { return false; }
         }
-        
+
+        protected override bool HasArtistAlbum {
+            get { return false; }
+        }
+
         public override bool ConfirmRemoveTracks {
             get { return false; }
         }
@@ -311,7 +720,38 @@ namespace Banshee.PlayQueue
         public override bool CanUnmap {
             get { return false; }
         }
-        
+
+        public override string PreferencesPageId {
+            get { return "play-queue"; }
+        }
+
+        private void InstallPreferences ()
+        {
+            pref_page = new Banshee.Preferences.SourcePage (PreferencesPageId, Name, "source-playlist", 500);
+
+            pref_section = pref_page.Add (new Section ());
+            pref_section.ShowLabel = false;
+            pref_section.Add (new SchemaPreference<int> (PlayedSongsNumberSchema,
+                Catalog.GetString ("Number of _played songs to show"), null, delegate {
+                    played_songs_number = PlayedSongsNumberSchema.Get ();
+                    UpdatePlayQueue ();
+                }
+            ));
+            pref_section.Add (new SchemaPreference<int> (UpcomingSongsNumberSchema,
+                Catalog.GetString ("Number of _upcoming songs to show"), null, delegate {
+                    upcoming_songs_number = UpcomingSongsNumberSchema.Get ();
+                    UpdatePlayQueue ();
+                }
+            ));
+        }
+
+        private void UninstallPreferences ()
+        {
+            pref_page.Dispose ();
+            pref_page = null;
+            pref_section = null;
+        }
+
         public static readonly SchemaEntry<bool> ClearOnQuitSchema = new SchemaEntry<bool> (
             "plugins.play_queue", "clear_on_quit",
             false,
@@ -319,21 +759,46 @@ namespace Banshee.PlayQueue
             "Clear the play queue when quitting"
         );
 
-        // Reserve strings in preparation for the forthcoming string freeze.
-        public void Strings ()
-        {
-            Catalog.GetString ("manual");
-            Catalog.GetString ("by song");
-            Catalog.GetString ("by album");
-            Catalog.GetString ("by artist");
-            Catalog.GetString ("by rating");
-            Catalog.GetString ("by score");
-            Catalog.GetString ("_Fill");
-            Catalog.GetString ("f_rom");
-            Catalog.GetString ("Refresh");
-            Catalog.GetString ("Refresh random tracks in the play queue");
-            Catalog.GetString ("Number of _played songs to show");
-            Catalog.GetString ("Number of _upcoming songs to show");
-        }
+        public static readonly SchemaEntry<int> CurrentTrackSchema = new SchemaEntry<int> (
+            "plugins.play_queue", "current_track",
+            0,
+            "Current Track",
+            "Current track in the Play Queue"
+        );
+
+        public static readonly SchemaEntry<int> CurrentOffsetSchema = new SchemaEntry<int> (
+            "plugins.play_queue", "current_offset",
+            0,
+            "Current Offset",
+            "Current offset of the Play Queue"
+        );
+
+        public static readonly SchemaEntry<int> PopulateModeSchema = new SchemaEntry<int> (
+            "plugins.play_queue", "populate_mode",
+            (int) PlaybackShuffleMode.Linear,
+            "Play Queue population mode",
+            "How (and if) the Play Queue should be randomly populated"
+        );
+
+        public static readonly SchemaEntry<string> PopulateFromSchema = new SchemaEntry<string> (
+            "plugins.play_queue", "populate_from",
+            ServiceManager.SourceManager.MusicLibrary.Name,
+            "Source to poplulate from",
+            "Name of the source to populate the the Play Queue from"
+        );
+
+        public static readonly SchemaEntry<int> PlayedSongsNumberSchema = new SchemaEntry<int> (
+            "plugins.play_queue", "played_songs_number",
+            10, 0, 100,
+            "Played Songs Number",
+            "Number of played songs to show in the Play Queue"
+        );
+
+        public static readonly SchemaEntry<int> UpcomingSongsNumberSchema = new SchemaEntry<int> (
+            "plugins.play_queue", "upcoming_songs_number",
+            10, 1, 100,
+            "Upcoming Songs Number",
+            "Number of upcoming songs to show in the Play Queue"
+        );
     }
 }
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueTrackListModel.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueTrackListModel.cs
new file mode 100644
index 0000000..8bf70a0
--- /dev/null
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueTrackListModel.cs
@@ -0,0 +1,67 @@
+//
+// PlayQueueTrackListModel.cs
+//
+// Author:
+//   Alexander Kojevnikov <alexander kojevnikov com>
+//
+// Copyright (C) 2009 Alexander Kojevnikov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+using Banshee.Database;
+using Banshee.Collection;
+using Banshee.Collection.Database;
+
+namespace Banshee.PlayQueue
+{
+    public class PlayQueueTrackListModel : DatabaseTrackListModel
+    {
+        private readonly PlayQueueSource source;
+
+        public PlayQueueTrackListModel (
+            BansheeDbConnection conn, IDatabaseTrackModelProvider provider, PlayQueueSource source) :
+            base (conn, provider, source)
+        {
+            this.source = source;
+        }
+
+        public override TrackInfo this[int index] {
+            get {
+                lock (this) {
+                    var track = cache.GetValue (index);
+                    if (track != null) {
+                        track.Enabled = source.IsTrackEnabled (index);
+                    }
+                    return track;
+                }
+            }
+        }
+
+        public override string UnfilteredQuery {
+           get {
+               return String.Format ("{0} AND ViewOrder >= {1}", base.UnfilteredQuery, source.Offset);
+           }
+       }
+   }
+}
\ No newline at end of file
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs
new file mode 100644
index 0000000..83d8362
--- /dev/null
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs
@@ -0,0 +1,106 @@
+// QueueableSourceComboBox.cs
+//
+// Authors:
+//   Alexander Kojevnikov <alexander kojevnikov com>
+//
+// Copyright (C) 2009 Alexander Kojevnikov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using Gtk;
+
+using Banshee.Library;
+using Banshee.ServiceStack;
+using Banshee.Sources;
+using Banshee.Sources.Gui;
+
+namespace Banshee.PlayQueue
+{
+    public class QueueableSourceComboBox : ComboBox
+    {
+        private readonly TreeModelFilter filter;
+
+        public QueueableSourceComboBox (string source_name)
+        {
+            SourceRowRenderer renderer = new SourceRowRenderer ();
+            renderer.ParentWidget = this;
+            PackStart (renderer, true);
+            SetCellDataFunc (renderer, new CellLayoutDataFunc (SourceRowRenderer.CellDataHandler));
+
+            var store = new SourceModel ();
+            filter = new TreeModelFilter (store, null);
+            filter.VisibleFunc = (model, iter) => IsQueueable (((SourceModel)model).GetSource (iter));
+            Model = filter;
+
+            store.Refresh ();
+
+            SetActiveSource (source_name);
+        }
+
+        private void SetActiveSource (string name)
+        {
+            TreeIter first;
+            if (filter.GetIterFirst (out first)) {
+                TreeIter iter = FindSource (name, first);
+                if (!iter.Equals (TreeIter.Zero)) {
+                    SetActiveIter (iter);
+                }
+            }
+        }
+
+        private bool IsQueueable (Source source)
+        {
+            return source != null && (
+                source is MusicLibrarySource || source.Parent is MusicLibrarySource ||
+                source is VideoLibrarySource || source.Parent is VideoLibrarySource);
+        }
+
+        private TreeIter FindSource (string name, TreeIter iter)
+        {
+            do {
+                var source = filter.GetValue (iter, 0) as ISource;
+                if (source != null && source.Name == name) {
+                    return iter;
+                }
+
+                TreeIter citer;
+                if (filter.IterChildren (out citer, iter)) {
+                    var yiter = FindSource (name, citer);
+                    if (!yiter.Equals (TreeIter.Zero)) {
+                        return yiter;
+                    }
+                }
+            } while (filter.IterNext (ref iter));
+
+            return TreeIter.Zero;
+        }
+
+        public ITrackModelSource Source {
+            get {
+                TreeIter iter;
+                if (GetActiveIter (out iter)) {
+                    return filter.GetValue(iter, 0) as ITrackModelSource;
+                }
+                return null;
+            }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.PlayQueue/Makefile.am b/src/Extensions/Banshee.PlayQueue/Makefile.am
index 68ca5cc..fd5b24b 100644
--- a/src/Extensions/Banshee.PlayQueue/Makefile.am
+++ b/src/Extensions/Banshee.PlayQueue/Makefile.am
@@ -4,9 +4,12 @@ LINK = $(REF_EXTENSION_PLAYQUEUE)
 INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
 
 SOURCES =  \
+	Banshee.PlayQueue/HeaderWidget.cs \
 	Banshee.PlayQueue/IPlayQueue.cs \
 	Banshee.PlayQueue/PlayQueueActions.cs \
-	Banshee.PlayQueue/PlayQueueSource.cs 
+	Banshee.PlayQueue/PlayQueueSource.cs \
+	Banshee.PlayQueue/PlayQueueTrackListModel.cs \
+	Banshee.PlayQueue/QueueableSourceComboBox.cs
 
 RESOURCES =  \
 	Banshee.PlayQueue.addin.xml \
diff --git a/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml b/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml
index 06c28cc..74bdd6f 100644
--- a/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml
+++ b/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml
@@ -1,6 +1,7 @@
 <ui>
     <toolbar name="HeaderToolbar">
         <placeholder name="SourceActions">
+            <toolitem action="RefreshPlayQueueAction" />
             <toolitem action="ClearPlayQueueAction" />
         </placeholder>
     </toolbar>
diff --git a/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml b/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml
index 0459c6c..eb95674 100644
--- a/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml
+++ b/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml
@@ -1,7 +1,9 @@
 <ui>
     <popup name="PlayQueueContextMenu">
+        <menuitem action="RefreshPlayQueueAction"></menuitem>
         <menuitem action="ClearPlayQueueAction"></menuitem>
         <menuitem action="ClearPlayQueueOnQuitAction"></menuitem>
+        <menuitem name="SourcePreferences" action="SourcePreferencesAction"/>
     </popup>
     <popup name="TrackContextMenu" action="TrackContextMenuAction">
         <placeholder name="AboveAddToPlaylist">
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
index b1e663b..9863da6 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
@@ -98,6 +98,7 @@ namespace Banshee.Podcasting.Gui
             MediaTypes = TrackMediaAttributes.Podcast;
             NotMediaTypes = TrackMediaAttributes.AudioBook;
             SyncCondition = "(substr(CoreTracks.Uri, 0, 4) != 'http' AND CoreTracks.PlayCount = 0)";
+            TrackModel.Reloaded += OnReloaded;
 
             Properties.SetString ("Icon.Name", "podcast");
             
@@ -197,7 +198,14 @@ namespace Banshee.Podcasting.Gui
                 AfterInitialized ();
             }
         }
-        
+
+        void OnReloaded(object sender, EventArgs e)
+        {
+            for (int i=0; i < TrackModel.Count; i++) {
+                PodcastTrackInfo.From (TrackModel[i]);
+            }
+        }
+
         // Probably don't want this -- do we want to allow actually removing the item?  It will be
         // repopulated the next time we update the podcast feed...
         /*protected override void DeleteTrack (DatabaseTrackInfo track)
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
index 410c804..ad92b35 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
@@ -64,6 +64,7 @@ namespace Banshee.Podcasting.Data
                 PodcastTrackInfo pi = track.ExternalObject as PodcastTrackInfo;
                 if (pi != null) {
                     track.ReleaseDate = pi.PublishedDate;
+                    track.Enabled = !pi.Item.IsRead;
                 }
                 return pi;
             }
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
index a724134..6e849de 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
@@ -84,7 +84,7 @@ namespace Banshee.Podcasting.Gui
             PodcastTrackInfo podcast = PodcastTrackInfo.From (BoundTrack);
             if (podcast != null) {
                 if (podcast.Activity == PodcastItemActivity.DownloadPending) {
-                    context.Sensitive = false;
+                    context.Opaque = false;
                 }
             }
             
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
index 312d2aa..6825962 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
@@ -47,27 +47,6 @@ using Banshee.Podcasting.Data;
 
 namespace Banshee.Podcasting.Gui
 {
-    public class PodcastItemView : TrackListView
-    {
-        public PodcastItemView () : base ()
-        {
-        }
-        
-        protected override void ColumnCellDataProvider (ColumnCell cell, object boundItem)
-        {
-            ColumnCellText text_cell = cell as ColumnCellText;
-            if (text_cell == null) {
-                return;
-            }
-            
-            PodcastTrackInfo podcast = PodcastTrackInfo.From (boundItem as TrackInfo);
-            if (podcast == null) {
-                return;
-            }
-            
-            text_cell.Opacity = podcast.IsNew ? 1.0 : 0.5;
-        }
-    }
     /*public class PodcastItemView : ListView<TrackInfo>
     {
         private PersistentColumnController columnController;
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
index b1e408b..66a6a60 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
@@ -53,7 +53,7 @@ namespace Banshee.Podcasting.Gui
 {
     public class PodcastSourceContents : FilteredListSourceContents, ITrackModelSourceContents
     {
-        private PodcastItemView track_view;
+        private TrackListView track_view;
         private PodcastFeedView feed_view;
         private PodcastUnheardFilterView unheard_view;
         private DownloadStatusFilterView download_view;
@@ -64,7 +64,7 @@ namespace Banshee.Podcasting.Gui
 
         protected override void InitializeViews ()
         {
-            SetupMainView (track_view = new PodcastItemView ());
+            SetupMainView (track_view = new TrackListView ());
             SetupFilterView (unheard_view = new PodcastUnheardFilterView ());
             SetupFilterView (download_view = new DownloadStatusFilterView ());
             SetupFilterView (feed_view = new PodcastFeedView ());
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
index 603ab61..bd4a625 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
@@ -42,7 +42,7 @@ namespace Hyena.Data.Gui
         private Gdk.Rectangle area;
         private Gdk.Rectangle clip;
         private bool text_as_foreground = false;
-        private bool sensitive = true;
+        private bool opaque = true;
         
         public Cairo.Context Context {
             get { return context; }
@@ -83,10 +83,10 @@ namespace Hyena.Data.Gui
             get { return text_as_foreground; }
             set { text_as_foreground = value; }
         }
-        
-        public bool Sensitive {
-            get { return sensitive; }
-            set { sensitive = value; }
+
+        public bool Opaque {
+            get { return opaque; }
+            set { opaque = value; }
         }
     }
 }
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
index bc5b6d4..484a292 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
@@ -44,7 +44,6 @@ namespace Hyena.Data.Gui
         private Pango.Weight font_weight = Pango.Weight.Normal;
         private Pango.EllipsizeMode ellipsize_mode = Pango.EllipsizeMode.End;
         private Pango.Alignment alignment = Pango.Alignment.Left;
-        private double opacity = 1.0;
         private int text_width;
         private int text_height;
         private string text_format = null;
@@ -81,7 +80,7 @@ namespace Hyena.Data.Gui
             context.Context.MoveTo (Spacing, ((int)cellHeight - text_height) / 2);
             Cairo.Color color = context.Theme.Colors.GetWidgetColor (
                 context.TextAsForeground ? GtkColorClass.Foreground : GtkColorClass.Text, state);
-            color.A = (!context.Sensitive) ? 0.3 : opacity;
+            color.A = context.Opaque ? 1.0 : 0.5;
             context.Context.Color = color;
 
             PangoCairoHelper.ShowLayout (context.Context, context.Layout);
@@ -172,11 +171,6 @@ namespace Hyena.Data.Gui
             set { ellipsize_mode = value; }
         }
         
-        public virtual double Opacity {
-            get { return opacity; }
-            set { opacity = value; }
-        }
-        
         internal static int ComputeRowHeight (Widget widget)
         {
             int w_width, row_height;
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
index e9fe295..891d557 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
@@ -130,42 +130,42 @@ namespace Hyena.Data.Gui
         public virtual IListModel<T> Model {
             get { return model; }
         }
-        
-        private string row_sensitive_property_name = "Sensitive";
-        private PropertyInfo row_sensitive_property_info;
-        bool row_sensitive_property_invalid = false;
-        
-        public string RowSensitivePropertyName {
-            get { return row_sensitive_property_name; }
-            set { 
-                if (value == row_sensitive_property_name) {
+
+        private string row_opaque_property_name = "Sensitive";
+        private PropertyInfo row_opaque_property_info;
+        bool row_opaque_property_invalid = false;
+
+        public string RowOpaquePropertyName {
+            get { return row_opaque_property_name; }
+            set {
+                if (value == row_opaque_property_name) {
                     return;
                 }
-                
-                row_sensitive_property_name = value;
-                row_sensitive_property_info = null;
-                row_sensitive_property_invalid = false;
-                
+
+                row_opaque_property_name = value;
+                row_opaque_property_info = null;
+                row_opaque_property_invalid = false;
+
                 InvalidateList ();
             }
         }
-        
-        private bool IsRowSensitive (object item)
+
+        private bool IsRowOpaque (object item)
         {
-            if (item == null || row_sensitive_property_invalid) {
+            if (item == null || row_opaque_property_invalid) {
                 return true;
             }
-         
-            if (row_sensitive_property_info == null || row_sensitive_property_info.ReflectedType != item.GetType ()) {
-                row_sensitive_property_info = item.GetType ().GetProperty (row_sensitive_property_name);
-                if (row_sensitive_property_info == null || row_sensitive_property_info.PropertyType != typeof (bool)) {
-                    row_sensitive_property_info = null;
-                    row_sensitive_property_invalid = true;
+
+            if (row_opaque_property_info == null || row_opaque_property_info.ReflectedType != item.GetType ()) {
+                row_opaque_property_info = item.GetType ().GetProperty (row_opaque_property_name);
+                if (row_opaque_property_info == null || row_opaque_property_info.PropertyType != typeof (bool)) {
+                    row_opaque_property_info = null;
+                    row_opaque_property_invalid = true;
                     return true;
                 }
             }
-            
-            return (bool)row_sensitive_property_info.GetValue (item, null);
+
+            return (bool)row_opaque_property_info.GetValue (item, null);
         }
         
         private string row_bold_property_name = "IsBold";
@@ -204,14 +204,5 @@ namespace Hyena.Data.Gui
             
             return (bool)row_bold_property_info.GetValue (item, null);
         }
-        
-        #pragma warning disable 0169
-        
-        private bool IsRowSensitive (int index)
-        {
-            return IsRowSensitive (model[index]);
-        }
-        
-        #pragma warning restore 0169
     }
 }
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
index d376eb7..d4c424e 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
@@ -131,7 +131,7 @@ namespace Hyena.Data.Gui
             cell_area.Height = header_rendering_alloc.Height;
             
             cell_context.Clip = clip;
-            cell_context.Sensitive = true;
+            cell_context.Opaque = true;
             cell_context.TextAsForeground = true;
 
             bool have_drawn_separator = false;
@@ -320,7 +320,7 @@ namespace Hyena.Data.Gui
             }
             
             object item = model[row_index];
-            bool sensitive = IsRowSensitive (item);
+            bool opaque = IsRowOpaque (item);
             bool bold = IsRowBold (item);
             
             Rectangle cell_area = new Rectangle ();
@@ -334,18 +334,18 @@ namespace Hyena.Data.Gui
                 
                 cell_area.Width = column_cache[ci].Width;
                 cell_area.X = column_cache[ci].X1 + area.X;
-                PaintCell (item, ci, row_index, cell_area, sensitive, bold, state, false);
+                PaintCell (item, ci, row_index, cell_area, opaque, bold, state, false);
             }
             
             if (pressed_column_is_dragging && pressed_column_index >= 0) {
                 cell_area.Width = column_cache[pressed_column_index].Width;
                 cell_area.X = pressed_column_x_drag + list_rendering_alloc.X -
                     list_interaction_alloc.X - HadjustmentValue;
-                PaintCell (item, pressed_column_index, row_index, cell_area, sensitive, bold, state, true);
+                PaintCell (item, pressed_column_index, row_index, cell_area, opaque, bold, state, true);
             }
         }
         
-        private void PaintCell (object item, int column_index, int row_index, Rectangle area, bool sensitive, bool bold,
+        private void PaintCell (object item, int column_index, int row_index, Rectangle area, bool opaque, bool bold,
             StateType state, bool dragging)
         {
             ColumnCell cell = column_cache[column_index].Column.GetCell (0);
@@ -368,7 +368,7 @@ namespace Hyena.Data.Gui
             cairo_context.Save ();
             cairo_context.Translate (area.X, area.Y);
             cell_context.Area = area;
-            cell_context.Sensitive = sensitive;
+            cell_context.Opaque = opaque;
             cell.Render (cell_context, dragging ? StateType.Normal : state, area.Width, area.Height);
             cairo_context.Restore ();
         }
diff --git a/src/Libraries/Hyena/Hyena.Collections/Selection.cs b/src/Libraries/Hyena/Hyena.Collections/Selection.cs
index 1d61e2f..75768dd 100644
--- a/src/Libraries/Hyena/Hyena.Collections/Selection.cs
+++ b/src/Libraries/Hyena/Hyena.Collections/Selection.cs
@@ -142,6 +142,19 @@ namespace Hyena.Collections
             OnChanged ();
         }
 
+        public void UnselectRange (int a, int b)
+        {
+            int start = Math.Min (a, b);
+            int end = Math.Max (a, b);
+
+            int i;
+            for (i = start; i <= end; i++) {
+                ranges.Remove (i);
+            }
+
+            OnChanged ();
+        }
+
         public virtual void SelectAll ()
         {
             SelectRange (0, max_index);



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