[banshee] Add a new, extensible context pane for Last.fm recommendations and



commit c8deeb24c5202861637b9ee8e8e4d5269629824b
Author: Gabriel Burt <gabriel burt gmail com>
Date:   Wed May 6 12:58:50 2009 -0500

    Add a new, extensible context pane for Last.fm recommendations and
    other context (Wikipedia, Amazon, etc).
---
 src/Clients/Nereid/Nereid/ViewContainer.cs         |   11 +-
 src/Core/Banshee.Core/Banshee.Core.csproj          |    2 +-
 src/Core/Banshee.Services/Banshee.Services.csproj  |    6 +-
 .../Banshee.ContextPane/BaseContextPage.cs         |   73 +++++
 .../Banshee.ContextPane/ContextPageManager.cs      |   66 +++++
 .../Banshee.ContextPane/ContextPane.cs             |  286 ++++++++++++++++++++
 .../Banshee.Gui.Widgets/TitledList.cs              |   67 +++++
 .../Banshee.ThickClient/Banshee.Gui/ViewActions.cs |    9 +
 .../Banshee.Sources.Gui/SourceView_DragAndDrop.cs  |    2 +-
 .../Banshee.ThickClient.addin.xml                  |    5 +
 .../Banshee.ThickClient/Banshee.ThickClient.csproj |   16 +-
 src/Core/Banshee.ThickClient/Makefile.am           |    6 +
 .../Resources/context-pane-close.png               |  Bin 0 -> 239 bytes
 .../Resources/context-pane-maximize.png            |  Bin 0 -> 237 bytes
 .../Resources/core-ui-actions-layout.xml           |    1 +
 .../Banshee.Lastfm.Radio/LastfmSourceContents.cs   |   32 +---
 .../Banshee.Lastfm.Recommendations/ContextPage.cs  |   63 +++++
 .../RecommendationActions.cs                       |   93 -------
 .../RecommendationPane.cs                          |   27 +-
 .../RecommendationService.cs                       |  160 -----------
 .../Banshee.Lastfm/Banshee.Lastfm.addin.xml        |    8 +-
 src/Extensions/Banshee.Lastfm/Makefile.am          |    3 +-
 .../Banshee.MediaWeb/Banshee.MediaWeb.addin.xml    |    2 +-
 src/Libraries/Hyena/Hyena.csproj                   |    7 +
 24 files changed, 633 insertions(+), 312 deletions(-)

diff --git a/src/Clients/Nereid/Nereid/ViewContainer.cs b/src/Clients/Nereid/Nereid/ViewContainer.cs
index 41c51c3..bd86c50 100644
--- a/src/Clients/Nereid/Nereid/ViewContainer.cs
+++ b/src/Clients/Nereid/Nereid/ViewContainer.cs
@@ -48,6 +48,7 @@ namespace Nereid
         private HBox header;
         private Label title_label;
         private Label search_label;
+        private Banshee.ContextPane.ContextPane context_pane;
         private VBox footer;
         
         private ISourceContents content;
@@ -112,6 +113,14 @@ namespace Nereid
             
             PackStart (header, false, false, 0);
             PackEnd (footer, false, false, 0);
+
+            context_pane = new Banshee.ContextPane.ContextPane ();
+            context_pane.ExpandHandler = b => {
+                SetChildPacking (content.Widget, !b, true, 0, PackType.Start);
+                SetChildPacking (context_pane, b, b, 0, PackType.End);
+            };
+            PackEnd (context_pane, false, false, 0);
+
             PackEnd (new ConnectedMessageBar (), false, true, 0);
         }
         
@@ -216,7 +225,7 @@ namespace Nereid
 
                 // Add and show the new one
                 if (value != null && value.Widget != null) {
-                    PackStart (value.Widget, true, true, 0);
+                    PackStart (value.Widget, !context_pane.Large, true, 0);
                     value.Widget.Show ();
                 }
                 
diff --git a/src/Core/Banshee.Core/Banshee.Core.csproj b/src/Core/Banshee.Core/Banshee.Core.csproj
index e5af764..805da77 100644
--- a/src/Core/Banshee.Core/Banshee.Core.csproj
+++ b/src/Core/Banshee.Core/Banshee.Core.csproj
@@ -69,7 +69,6 @@
     <Compile Include="Banshee.Collection\TrackFilterType.cs" />
     <Compile Include="Banshee.Base\PlatformHacks.cs" />
     <Compile Include="Banshee.Streaming\CommonTags.cs" />
-    <Compile Include="Banshee.Streaming\SaveTrackMetadataJob.cs" />
     <Compile Include="Banshee.Streaming\StreamTag.cs" />
     <Compile Include="Banshee.Streaming\StreamTagger.cs" />
     <Compile Include="Banshee.Streaming\StreamPlaybackError.cs" />
@@ -106,6 +105,7 @@
     <Compile Include="Banshee.IO\DirectoryScannerPipelineElement.cs" />
     <Compile Include="Banshee.Configuration\MemoryConfigurationClient.cs" />
     <Compile Include="Banshee.IO\ExtensionSet.cs" />
+    <Compile Include="Banshee.Base\Tests\CoverArtSpecTests.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Resources\contributors.xml">
diff --git a/src/Core/Banshee.Services/Banshee.Services.csproj b/src/Core/Banshee.Services/Banshee.Services.csproj
index 5021339..066f4a6 100644
--- a/src/Core/Banshee.Services/Banshee.Services.csproj
+++ b/src/Core/Banshee.Services/Banshee.Services.csproj
@@ -87,7 +87,6 @@
     <Compile Include="Banshee.Collection\ModelHelper.cs" />
     <Compile Include="Banshee.Collection\TrackListModel.cs" />
     <Compile Include="Banshee.MediaEngine\PlayerEngineService.cs" />
-    <Compile Include="Banshee.ServiceStack\UserJobManager.cs" />
     <Compile Include="Banshee.ServiceStack\IUserJob.cs" />
     <Compile Include="Banshee.ServiceStack\UserJobEventHandler.cs" />
     <Compile Include="Banshee.ServiceStack\UserJob.cs" />
@@ -228,7 +227,6 @@
     <Compile Include="Banshee.Collection.Database\QueryFilterInfo.cs" />
     <Compile Include="Banshee.Sources\IFilterableSource.cs" />
     <Compile Include="Banshee.MediaEngine\IVisualizationDataSource.cs" />
-    <Compile Include="Banshee.Collection\MoveOnInfoSaveJob.cs" />
     <Compile Include="Banshee.ServiceStack\IRegisterOnDemandService.cs" />
     <Compile Include="Banshee.Web\HttpRequest.cs" />
     <Compile Include="Banshee.Collection.Indexer\CollectionIndexerService.cs" />
@@ -252,8 +250,10 @@
     <Compile Include="Banshee.Preferences\SourcePage.cs" />
     <Compile Include="Banshee.ServiceStack\DbIteratorJob.cs" />
     <Compile Include="Banshee.Sources\SourceSortType.cs" />
-    <Compile Include="Banshee.Library\LibraryLocationPreference.cs" />
     <Compile Include="Banshee.Playlists.Formats\AsfReferencePlaylistFormat.cs" />
+    <Compile Include="Banshee.Metadata\SaveTrackMetadataJob.cs" />
+    <Compile Include="Banshee.Metadata\SaveTrackMetadataService.cs" />
+    <Compile Include="Banshee.ServiceStack\JobScheduler.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Banshee.Services.addin.xml" />
diff --git a/src/Core/Banshee.ThickClient/Banshee.ContextPane/BaseContextPage.cs b/src/Core/Banshee.ThickClient/Banshee.ContextPane/BaseContextPage.cs
new file mode 100644
index 0000000..20cf12a
--- /dev/null
+++ b/src/Core/Banshee.ThickClient/Banshee.ContextPane/BaseContextPage.cs
@@ -0,0 +1,73 @@
+//
+// BaseContextPage.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// 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 Gtk;
+
+using Banshee.Collection;
+
+namespace Banshee.ContextPane
+{
+    public enum ContextState {
+        NotLoaded,
+        Loading,
+        Loaded
+    };
+
+    public abstract class BaseContextPage
+    {
+        private ContextState state = ContextState.NotLoaded;
+
+        public event Action<ContextState> StateChanged;
+
+        public ContextState State {
+            get { return state; }
+            protected set {
+                if (state == value)
+                    return;
+
+                state = value;
+
+                var handler = StateChanged;
+                if (handler != null) {
+                    handler (state);
+                };
+            }
+        }
+
+        public string Id { get; protected set; }
+        public string Name { get; protected set;}
+        public string [] IconNames { get; protected set;}
+
+        public abstract Widget Widget { get; }
+        public abstract void SetTrack (TrackInfo track);
+        
+        public virtual void Dispose () {}
+    }
+}
diff --git a/src/Core/Banshee.ThickClient/Banshee.ContextPane/ContextPageManager.cs b/src/Core/Banshee.ThickClient/Banshee.ContextPane/ContextPageManager.cs
new file mode 100644
index 0000000..f45d314
--- /dev/null
+++ b/src/Core/Banshee.ThickClient/Banshee.ContextPane/ContextPageManager.cs
@@ -0,0 +1,66 @@
+//
+// ContextPageManager.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// 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 Mono.Addins;
+using Gtk;
+using Banshee.Gui;
+
+namespace Banshee.ContextPane
+{
+    public class ContextPageManager
+    {
+        private ContextPane pane;
+        private Dictionary<string, BaseContextPage> pages = new Dictionary<string, BaseContextPage> ();
+
+        public ContextPageManager (ContextPane pane)
+        {
+            this.pane = pane;
+            Mono.Addins.AddinManager.AddExtensionNodeHandler ("/Banshee/ThickClient/ContextPage", OnExtensionChanged);
+        }
+
+        private void OnExtensionChanged (object o, ExtensionNodeEventArgs args) 
+        {
+            TypeExtensionNode node = (TypeExtensionNode)args.ExtensionNode;
+            
+            if (args.Change == ExtensionChange.Add) {
+                var page = (BaseContextPage) node.CreateInstance ();
+                pane.AddPage (page);
+                pages.Add (node.Id, page);
+            } else {
+                if (pages.ContainsKey (node.Id)) {
+                    var page = pages[node.Id];
+                    pane.RemovePage (page);
+                    page.Dispose ();
+                    pages.Remove (node.Id);
+                }
+            }
+        }
+    }
+}
diff --git a/src/Core/Banshee.ThickClient/Banshee.ContextPane/ContextPane.cs b/src/Core/Banshee.ThickClient/Banshee.ContextPane/ContextPane.cs
new file mode 100644
index 0000000..e65ce6d
--- /dev/null
+++ b/src/Core/Banshee.ThickClient/Banshee.ContextPane/ContextPane.cs
@@ -0,0 +1,286 @@
+//
+// ContextPane.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// 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 Mono.Unix;
+
+using Gtk;
+
+using Hyena.Gui;
+using Hyena.Widgets;
+
+using Banshee.Base;
+using Banshee.Configuration;
+using Banshee.Collection;
+using Banshee.ServiceStack;
+using Banshee.MediaEngine;
+using Banshee.Gui;
+
+namespace Banshee.ContextPane
+{
+    public class ContextPane : Gtk.HBox
+    {
+        private object tooltip_host = TooltipSetter.CreateHost ();
+
+        private Gtk.Notebook notebook;
+        private VBox vbox;
+        private bool large = false;
+
+        private RoundedFrame no_active;
+        private RoundedFrame loading;
+
+        private RadioButton radio_group = new RadioButton (null, "");
+
+        private Dictionary<BaseContextPage, RadioButton> pane_tabs = new Dictionary<BaseContextPage, RadioButton> ();
+        private Dictionary<BaseContextPage, Widget> pane_pages = new Dictionary<BaseContextPage, Widget> ();
+        private List<BaseContextPage> pages = new List<BaseContextPage> ();
+        private BaseContextPage active_page;
+
+        private const int MIN_HEIGHT = 200;
+
+        private Action<bool> expand_handler;
+        public Action<bool> ExpandHandler {
+            set { expand_handler = value; }
+        }
+
+        public bool Large {
+            get { return large; }
+        }
+        
+        public ContextPane ()
+        {
+            notebook = new Notebook () {
+                ShowBorder = false,
+                ShowTabs = false
+            };
+
+            vbox = new VBox ();
+
+            HBox hbox = new HBox ();
+            var max = new Button (new Image (IconThemeUtils.LoadIcon ("context-pane-maximize", 7)));
+            max.Clicked += (o, a) => { large = !large; expand_handler (large); };
+            TooltipSetter.Set (tooltip_host, max, Catalog.GetString ("Make the context pane larger or smaller"));
+            
+            var close = new Button (new Image (IconThemeUtils.LoadIcon ("context-pane-close", 7)));
+            close.Clicked += (o, a) => ShowAction.Activate ();
+            TooltipSetter.Set (tooltip_host, close, Catalog.GetString ("Hide context pane"));
+            
+            max.Relief = close.Relief = ReliefStyle.None;
+            hbox.PackStart (max, false, false, 0);
+            hbox.PackStart (close, false, false, 0);
+            vbox.PackStart (hbox, false, false, 0);
+
+            // 'No active track' and 'Loading' widgets
+            no_active = new RoundedFrame ();
+            no_active.Add (new Label () { Markup = "<b>Context pane waiting for playback to begin...</b>" });
+            no_active.ShowAll ();
+            notebook.Add (no_active);
+
+            loading = new RoundedFrame ();
+            loading.Add (new Label () { Markup = "<b>Loading...</b>" });
+            loading.ShowAll ();
+            notebook.Add (loading);
+
+            PackStart (notebook, true, true, 0);
+            PackStart (vbox, false, false, 6);
+
+            notebook.Show ();
+            vbox.ShowAll ();
+            
+            HeightRequest = MIN_HEIGHT;
+
+            // TODO remember/restore the last page
+
+            new ContextPageManager (this);
+
+            Enabled = ShowSchema.Get ();
+            ShowAction.Activated += OnShowContextPane;
+
+            ServiceManager.PlayerEngine.ConnectEvent (OnPlayerEvent, PlayerEvent.StartOfStream | PlayerEvent.TrackInfoUpdated);
+        }
+
+        private void OnPlayerEvent (PlayerEventArgs args)
+        {
+            if (Enabled) {
+                SetCurrentTrackForActivePage ();
+            }
+        }
+
+        private void SetCurrentTrackForActivePage ()
+        {
+            TrackInfo track = ServiceManager.PlayerEngine.CurrentTrack;
+            if (track != null && active_page != null) {
+                active_page.SetTrack (track);
+            }
+        }
+
+        private void OnActivePageStateChanged (ContextState state)
+        {
+            if (state == ContextState.NotLoaded)
+                notebook.CurrentPage = notebook.PageNum (no_active);
+            else if (state == ContextState.Loading)
+                notebook.CurrentPage = notebook.PageNum (loading);
+            else if (state == ContextState.Loaded)
+                notebook.CurrentPage = notebook.PageNum (pane_pages[active_page]);
+        }
+
+        private Gtk.ToggleAction ShowAction {
+            get { return ServiceManager.Get<InterfaceActionService> ().ViewActions["ShowContextPaneAction"] as ToggleAction; }
+        }
+
+        private void OnShowContextPane (object o, EventArgs args)
+        {
+            Enabled = ShowAction.Active;
+        }
+
+        private bool Enabled {
+            get { return ShowSchema.Get (); }
+            set {
+                ShowSchema.Set (value);
+                SetCurrentTrackForActivePage ();
+                UpdateVisibility ();
+            }
+        }
+
+        private void UpdateVisibility ()
+        {
+            int npages = pages.Count;
+            bool enabled = Enabled;
+
+            ShowAction.Sensitive = npages > 0;
+
+            if (enabled && npages > 0) {
+                Show ();
+            } else {
+                if (expand_handler != null) {
+                    expand_handler (false);
+                }
+                large = false;
+                Hide ();
+            }
+            
+            vbox.Visible = true;//enabled && npages > 1;
+        }
+
+        private void SetActivePage (BaseContextPage page)
+        {
+            if (page == null || page == active_page)
+                return;
+
+            if (active_page != null)
+                active_page.StateChanged -= OnActivePageStateChanged;
+
+            active_page = page;
+            active_page.StateChanged += OnActivePageStateChanged;
+            OnActivePageStateChanged (active_page.State);
+            SetCurrentTrackForActivePage ();
+        }
+
+        public void AddPage (BaseContextPage page)
+        {
+            Hyena.Log.DebugFormat ("Adding context page {0}", page.Id);
+            var frame = new Hyena.Widgets.RoundedFrame ();
+            frame.Add (page.Widget);
+            frame.Show ();
+
+            // TODO implement DnD?
+            /*if (page is ITrackContextPage) {
+                Gtk.Drag.DestSet (frame, DestDefaults.Highlight | DestDefaults.Motion,
+                                  new TargetEntry [] { Banshee.Gui.DragDrop.DragDropTarget.UriList },
+                                  Gdk.DragAction.Default);
+                frame.DragDataReceived += delegate(object o, DragDataReceivedArgs args) {
+                };
+            }*/
+            
+            page.Widget.Show ();
+            notebook.AppendPage (frame, null);
+            pane_pages[page] = frame;
+
+            // Setup the tab-like button that switches the notebook to this page
+            var tab_image = new Image (IconThemeUtils.LoadIcon (22, page.IconNames));
+            var toggle_button = new RadioButton (radio_group) {
+                Child = tab_image,
+                DrawIndicator = false,
+                Relief = ReliefStyle.None
+            };
+            TooltipSetter.Set (tooltip_host, toggle_button, page.Name);
+            toggle_button.Clicked += (s, e) => {
+                if (pane_pages.ContainsKey (page)) {
+                    if (page.State == ContextState.Loaded) {
+                        notebook.CurrentPage = notebook.PageNum (pane_pages[page]);
+                    }
+                    SetActivePage (page);
+                }
+            };
+            toggle_button.ShowAll ();
+            vbox.PackStart (toggle_button, false, false, 0);
+            pane_tabs[page] = toggle_button;
+
+            pages.Add (page);
+
+            if (pages.Count == 1) {
+                SetActivePage (page);
+                toggle_button.Active = true;
+            }
+
+            UpdateVisibility ();
+        }
+
+        public void RemovePage (BaseContextPage page)
+        {
+            Hyena.Log.DebugFormat ("Removing context page {0}", page.Id);
+            // Remove the notebook page
+            notebook.RemovePage (notebook.PageNum (pane_pages[page]));
+            pane_pages.Remove (page);
+
+            // Remove the tab button
+            bool was_active = pane_tabs[page].Active;
+            vbox.Remove (pane_tabs[page]);
+            pane_tabs.Remove (page);
+
+            pages.Remove (page);
+
+            // Set a new page as the default
+            if (was_active && pages.Count > 0) {
+                SetActivePage (pages[0]);
+                pane_tabs[active_page].Active = true;
+            }
+
+            UpdateVisibility ();
+        }
+
+        internal static readonly SchemaEntry<bool> ShowSchema = new SchemaEntry<bool>(
+            "interface", "show_context_pane",
+            true,
+            "Show context pane",
+            "Show context pane for the currently playing track"
+        );
+    }
+}
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TitledList.cs b/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TitledList.cs
new file mode 100644
index 0000000..afc9e92
--- /dev/null
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TitledList.cs
@@ -0,0 +1,67 @@
+//
+// TitledList.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// 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 Gtk;
+
+namespace Banshee.Gui.Widgets
+{
+    public class TitledList : VBox
+    {
+        private Label title;
+
+        public string Title {
+            get { return title.Text; }
+            set {
+                title.Markup = String.Format ("<b>{0}</b>", GLib.Markup.EscapeText (value));
+            }
+        }
+
+        public int TitleWidthChars {
+            get { return title.WidthChars; }
+            set { title.WidthChars = value; }
+        }
+
+        public TitledList (string title_str) : base (false, 3)
+        {
+            title = new Label ();
+            title.Xalign = 0;
+            title.Ellipsize = Pango.EllipsizeMode.End;
+            Title = title_str;
+
+            PackStart (title, false, false, 0);
+            title.Show ();
+
+            StyleSet += delegate {
+                title.ModifyBg (StateType.Normal, Style.Base (StateType.Normal));
+                title.ModifyFg (StateType.Normal, Style.Text (StateType.Normal));
+            };
+        }
+    }
+}
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui/ViewActions.cs b/src/Core/Banshee.ThickClient/Banshee.Gui/ViewActions.cs
index c66eb86..97fdef6 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Gui/ViewActions.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui/ViewActions.cs
@@ -66,6 +66,15 @@ namespace Banshee.Gui
                    Catalog.GetString ("View the graphical equalizer"), OnShowEqualizer)
             });
 
+            Add (new ToggleActionEntry [] {
+                new ToggleActionEntry (
+                   "ShowContextPaneAction", null,
+                   Catalog.GetString ("_Context Pane"), "",
+                   Catalog.GetString ("Show the context pane beneath the track list"), null,
+                   Banshee.ContextPane.ContextPane.ShowSchema.Get ()
+                )
+            });
+
             AddImportant (new ToggleActionEntry [] {
                 new ToggleActionEntry ("FullScreenAction", "gtk-fullscreen",
                     Catalog.GetString ("_Fullscreen"), "F",
diff --git a/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView_DragAndDrop.cs b/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView_DragAndDrop.cs
index e7d02cc..b6181f9 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView_DragAndDrop.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceView_DragAndDrop.cs
@@ -231,7 +231,7 @@ namespace Banshee.Sources.Gui
                     // If the drag source is not the track list, it's a filter list, and instead of
                     // only merging the track model's selected tracks, we should merge all the tracks
                     // currently matching the active filters.
-                    bool from_filter = !(Gtk.Drag.GetSourceWidget (context) is Banshee.Collection.Gui.TrackListView);
+                    bool from_filter = !(Gtk.Drag.GetSourceWidget (context) is Banshee.Collection.Gui.BaseTrackListView);
                     drop_source.MergeSourceInput (
                         ServiceManager.SourceManager.ActiveSource,
                         from_filter ? SourceMergeType.Source : SourceMergeType.ModelSelection
diff --git a/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml b/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
index 9b094b0..cc20699 100644
--- a/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
+++ b/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
@@ -40,6 +40,11 @@
     <Description>Defines a new GTK+ source view, possibly in conjunction with a Source extension.</Description>
     <ExtensionNode name="SourceView"/>
   </ExtensionPoint>
+
+  <ExtensionPoint path="/Banshee/ThickClient/ContextPage">
+    <Description>Defines a new GTK+ context page, for showing contextual information beneath the main track source view.</Description>
+    <ExtensionNode name="ContextPage"/>
+  </ExtensionPoint>
   
   <ExtensionPoint path="/Banshee/Gui/TrackEditor/NotebookPage">
     <Description>Defines a new notebook page for the track editor.</Description>
diff --git a/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj b/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
index 5133453..bd301e5 100644
--- a/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
+++ b/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
@@ -70,6 +70,12 @@
     <EmbeddedResource Include="Resources\browser-album-cover.png">
       <LogicalName>browser-album-cover.png</LogicalName>
     </EmbeddedResource>
+    <EmbeddedResource Include="Resources\context-pane-close.png">
+      <LogicalName>context-pane-close.png</LogicalName>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Resources\context-pane-maximize.png">
+      <LogicalName>context-pane-maximize.png</LogicalName>
+    </EmbeddedResource>
     <EmbeddedResource Include="Resources\core-ui-actions-layout.xml">
       <LogicalName>core-ui-actions-layout.xml</LogicalName>
     </EmbeddedResource>
@@ -227,6 +233,11 @@
     <Compile Include="Banshee.Gui.TrackEditor\SortingPage.cs" />
     <Compile Include="Banshee.Gui.Widgets\TaskStatusIcon.cs" />
     <Compile Include="Banshee.Preferences.Gui\PageComboBox.cs" />
+    <Compile Include="Banshee.Gui.TrackEditor\LicenseEntry.cs" />
+    <Compile Include="Banshee.Gui.Widgets\TitledList.cs" />
+    <Compile Include="Banshee.ContextPane\BaseContextPage.cs" />
+    <Compile Include="Banshee.ContextPane\ContextPageManager.cs" />
+    <Compile Include="Banshee.ContextPane\ContextPane.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
@@ -245,4 +256,7 @@
       </Properties>
     </MonoDevelop>
   </ProjectExtensions>
-</Project>
\ No newline at end of file
+  <ItemGroup>
+    <Folder Include="Banshee.ContextPane\" />
+  </ItemGroup>
+</Project>
diff --git a/src/Core/Banshee.ThickClient/Makefile.am b/src/Core/Banshee.ThickClient/Makefile.am
index 317e690..d53e769 100644
--- a/src/Core/Banshee.ThickClient/Makefile.am
+++ b/src/Core/Banshee.ThickClient/Makefile.am
@@ -32,6 +32,9 @@ SOURCES =  \
 	Banshee.Collection.Gui/TrackFilterListView.cs \
 	Banshee.Collection.Gui/TrackListView.cs \
 	Banshee.Collection.Gui/XmlColumnController.cs \
+	Banshee.ContextPane/BaseContextPage.cs \
+	Banshee.ContextPane/ContextPageManager.cs \
+	Banshee.ContextPane/ContextPane.cs \
 	Banshee.Equalizer.Gui/EqualizerBandScale.cs \
 	Banshee.Equalizer.Gui/EqualizerLevelsBox.cs \
 	Banshee.Equalizer.Gui/EqualizerPresetComboBox.cs \
@@ -91,6 +94,7 @@ SOURCES =  \
 	Banshee.Gui.Widgets/PlaylistMenuItem.cs \
 	Banshee.Gui.Widgets/RepeatActionButton.cs \
 	Banshee.Gui.Widgets/TaskStatusIcon.cs \
+	Banshee.Gui.Widgets/TitledList.cs \
 	Banshee.Gui.Widgets/TrackInfoDisplay.cs \
 	Banshee.Gui.Widgets/UserJobTile.cs \
 	Banshee.Gui.Widgets/UserJobTileHost.cs \
@@ -153,6 +157,8 @@ RESOURCES =  \
 	Resources/banshee-dialogs.glade \
 	Resources/banshee-logo.png \
 	Resources/browser-album-cover.png \
+	Resources/context-pane-close.png \
+	Resources/context-pane-maximize.png \
 	Resources/core-ui-actions-layout.xml \
 	Resources/jcastro.png
 
diff --git a/src/Core/Banshee.ThickClient/Resources/context-pane-close.png b/src/Core/Banshee.ThickClient/Resources/context-pane-close.png
new file mode 100644
index 0000000..71016e2
Binary files /dev/null and b/src/Core/Banshee.ThickClient/Resources/context-pane-close.png differ
diff --git a/src/Core/Banshee.ThickClient/Resources/context-pane-maximize.png b/src/Core/Banshee.ThickClient/Resources/context-pane-maximize.png
new file mode 100644
index 0000000..225c2ab
Binary files /dev/null and b/src/Core/Banshee.ThickClient/Resources/context-pane-maximize.png differ
diff --git a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
index 2fce039..e4ea280 100644
--- a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
+++ b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
@@ -60,6 +60,7 @@
         <!--<menuitem name="ShowCoverArt" action="ShowCoverArtAction"/>-->
       <placeholder name="BrowserViews"/>
       <placeholder name="ViewMenuAdditions"/>
+      <menuitem name="ShowContextPane" action="ShowContextPaneAction"/>
       <menuitem name="FullScreen" action="FullScreenAction"/>
       <separator/>
       <menuitem name="ShowEqualizer" action="ShowEqualizerAction"/>
diff --git a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs
index a2c6786..3060521 100644
--- a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs
+++ b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSourceContents.cs
@@ -11,6 +11,7 @@ using Banshee.ServiceStack;
 using Banshee.Collection;
 using Banshee.Collection.Gui;
 using Banshee.Gui;
+using Banshee.Gui.Widgets;
 using Banshee.Sources.Gui;
 using Banshee.Web;
 
@@ -267,36 +268,5 @@ namespace Banshee.Lastfm.Radio
         }
     }
 
-    public class TitledList : VBox
-    {
-        private Label title;
-
-        public string Title {
-            get { return title.Text; }
-            set {
-                title.Markup = String.Format ("<b>{0}</b>", GLib.Markup.EscapeText (value));
-            }
-        }
 
-        public int TitleWidthChars {
-            get { return title.WidthChars; }
-            set { title.WidthChars = value; }
-        }
-
-        public TitledList (string title_str) : base (false, 3)
-        {
-            title = new Label ();
-            title.Xalign = 0;
-            title.Ellipsize = Pango.EllipsizeMode.End;
-            Title = title_str;
-
-            PackStart (title, false, false, 0);
-            title.Show ();
-
-            StyleSet += delegate {
-                title.ModifyBg (StateType.Normal, Style.Base (StateType.Normal));
-                title.ModifyFg (StateType.Normal, Style.Text (StateType.Normal));
-            };
-        }
-    }
 }
diff --git a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/ContextPage.cs b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/ContextPage.cs
new file mode 100644
index 0000000..6040dd9
--- /dev/null
+++ b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/ContextPage.cs
@@ -0,0 +1,63 @@
+//
+// ContextPage.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// 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 Mono.Unix;
+
+using Gtk;
+
+using Banshee.ContextPane;
+
+namespace Banshee.Lastfm.Recommendations
+{
+    public class ContextPage : BaseContextPage
+    {
+        public ContextPage ()
+        {
+            Id = "lastfm-recommendations";
+            Name = Catalog.GetString ("Last.fm Recommendations");
+            IconNames = new string[] { "lastfm-audioscrobbler" };
+        }
+
+        private RecommendationPane recs;
+        public override Widget Widget {
+            get { return recs ?? (recs = new RecommendationPane (this)); }
+        }
+
+        internal void SetState (ContextState state)
+        {
+            State = state;
+        }
+
+        public override void SetTrack (Banshee.Collection.TrackInfo track)
+        {
+            recs.Artist = track.ArtistName;
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationActions.cs b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationActions.cs
deleted file mode 100644
index a41d64e..0000000
--- a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationActions.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-// RecommendationActions.cs
-//
-// Authors:
-//   Gabriel Burt <gburt novell com>
-//
-// Copyright (C) 2007-2008 Novell, Inc.
-//
-// 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 Gtk;
-
-using Mono.Unix;
-
-using Lastfm;
-using Lastfm.Gui;
-using SortType = Hyena.Data.SortType;
-
-using Banshee.Base;
-using Banshee.Sources;
-using Banshee.Widgets;
-using Banshee.MediaEngine;
-using Banshee.Database;
-using Banshee.Configuration;
-using Banshee.ServiceStack;
-using Banshee.Gui;
-using Banshee.Collection;
-using Banshee.PlaybackController;
-
-using Browser = Banshee.Web.Browser;
-
-namespace Banshee.Lastfm.Recommendations
-{
-    public class RecommendationActions : BansheeActionGroup
-    {
-        private uint actions_id;
-        private RecommendationService service;
-
-        public RecommendationActions (RecommendationService service) : base (ServiceManager.Get<InterfaceActionService> (), "LastfmRecommendations")
-        {
-            this.service = service;
-
-            Add (new ToggleActionEntry [] {
-                new ToggleActionEntry (
-                    "ShowRecommendationAction", null,
-                    Catalog.GetString("Show Recommendations"), "<control>R",
-                    Catalog.GetString("Show Recommendations"), OnToggleShow,
-                    RecommendationService.ShowSchema.Get ()
-                )
-            });
-
-            actions_id = Actions.UIManager.AddUiFromResource ("RecommendationMenu.xml");
-            Actions.AddActionGroup (this);
-        }
-
-        public override void Dispose ()
-        {
-            Actions.UIManager.RemoveUi (actions_id);
-            Actions.RemoveActionGroup (this);
-            base.Dispose ();
-        }
-
-#region Action Handlers 
-
-        private void OnToggleShow(object o, EventArgs args)
-        {
-            service.RecommendationsShown = (o as ToggleAction).Active;
-        }
-
-#endregion
-
-    }
-}
diff --git a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationPane.cs b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationPane.cs
index 1e23fba..a2c2961 100644
--- a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationPane.cs
+++ b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationPane.cs
@@ -51,6 +51,7 @@ using Banshee.Base;
 using Banshee.Configuration;
 using Banshee.ServiceStack;
 using Banshee.Gui;
+using Banshee.Gui.Widgets;
 using Banshee.Networking;
 
 using Banshee.Collection;
@@ -62,8 +63,9 @@ using Banshee.Lastfm.Radio;
 
 namespace Banshee.Lastfm.Recommendations
 {
-    public class RecommendationPane : Hyena.Widgets.RoundedFrame
+    public class RecommendationPane : HBox
     {
+        private ContextPage context_page;
         private HBox main_box;
         private MessagePane no_artists_pane;
         private TitledList artist_box;
@@ -93,11 +95,11 @@ namespace Banshee.Lastfm.Recommendations
         private bool ready = false;
         private bool refreshing = false;
         private bool show_when_ready = true;
-        public bool ShowWhenReady {
+        private bool ShowWhenReady {
             get { return show_when_ready; }
             set {
                 show_when_ready = value;
-                UpdateVisiblity ();
+                ShowIfReady ();
                 
                 if (!show_when_ready) {
                     CancelTasks ();
@@ -107,7 +109,7 @@ namespace Banshee.Lastfm.Recommendations
             }
         }
         
-        private void UpdateVisiblity ()
+        private void ShowIfReady ()
         {
             if (ShowWhenReady && ready) {
                 ShowAll ();
@@ -119,7 +121,6 @@ namespace Banshee.Lastfm.Recommendations
             get { return artist; }
             set {
                 if (artist == value) {
-                    UpdateVisiblity ();
                     return;
                 }
                 
@@ -133,10 +134,7 @@ namespace Banshee.Lastfm.Recommendations
                     }
                 }
                 
-                if (String.IsNullOrEmpty (artist)) {
-                    Hide ();
-                } else {
-                    HideWithTimeout ();
+                if (!String.IsNullOrEmpty (artist)) {
                     RefreshRecommendations ();
                 }
             }
@@ -148,6 +146,7 @@ namespace Banshee.Lastfm.Recommendations
             
             if (show_when_ready && !String.IsNullOrEmpty (Artist)) {
                 refreshing = true;
+                context_page.SetState (Banshee.ContextPane.ContextState.Loading);
                 Banshee.Kernel.Scheduler.Schedule (new RefreshRecommendationsJob (this, Artist));
             }
         }
@@ -171,12 +170,14 @@ namespace Banshee.Lastfm.Recommendations
             return false;
         }
 
-        public RecommendationPane () : base ()
+        public RecommendationPane (ContextPage contextPage) : base ()
         {
-            main_box = new HBox ();
+            this.context_page = contextPage;
+            main_box = this;
             main_box.BorderWidth = 5;
 
             artist_box = new TitledList (Catalog.GetString ("Recommended Artists"));
+            artist_box.ShowAll ();
             similar_artists_view = new TileView (2);
             similar_artists_view_sw = new Gtk.ScrolledWindow ();
             similar_artists_view_sw.SetPolicy (PolicyType.Never, PolicyType.Automatic);
@@ -219,8 +220,6 @@ namespace Banshee.Lastfm.Recommendations
             main_box.PackStart (track_box, false, false, 5);
             
             no_artists_pane.Hide ();
-            
-            Add (main_box);
         }
         
         private void OnSideSizeAllocated (object o, SizeAllocatedArgs args)
@@ -345,7 +344,7 @@ namespace Banshee.Lastfm.Recommendations
                 
                 ready = true;
                 refreshing = false;
-                UpdateVisiblity ();
+                context_page.SetState (Banshee.ContextPane.ContextState.Loaded);
             });
         }
         
diff --git a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationService.cs b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationService.cs
deleted file mode 100644
index 0d547c1..0000000
--- a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Recommendations/RecommendationService.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-//
-// RecommendationService.cs
-//
-// Authors:
-//   Aaron Bockover <aaron abock org>
-//   Fredrik Hedberg
-//   Gabriel Burt <gburt novell com>
-//   Lukas Lipka
-//
-// Copyright (C) 2005-2008 Novell, Inc.
-//
-// 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.IO;
-using System.Net;
-using System.Text;
-using System.Security.Cryptography;
-using Gtk;
-using Mono.Unix;
-
-using Hyena;
-
-using Lastfm;
-using Lastfm.Gui;
-
-using Banshee.MediaEngine;
-using Banshee.Base;
-using Banshee.Configuration;
-using Banshee.ServiceStack;
-using Banshee.Gui;
-using Banshee.Networking;
-
-using Banshee.Collection;
-
-using Browser = Lastfm.Browser;
-
-namespace Banshee.Lastfm.Recommendations
-{
-    public class RecommendationService : IExtensionService
-    {
-        private RecommendationPane pane;
-        private RecommendationActions actions;
-
-        void IExtensionService.Initialize ()
-        {
-            pane = new RecommendationPane ();
-
-            BaseClientWindow nereid = ServiceManager.Get<IService> ("NereidPlayerInterface") as Banshee.Gui.BaseClientWindow;
-            if (nereid != null) {
-                nereid.ViewContainer.PackEnd (pane, false, false, 0);
-            }
-
-            actions = new RecommendationActions (this);
-
-            ServiceManager.PlaybackController.SourceChanged += OnSourceChanged;
-            ServiceManager.SourceManager.ActiveSourceChanged += OnActiveSourceChanged;
-            ServiceManager.PlayerEngine.ConnectEvent (OnPlayerEvent, PlayerEvent.EndOfStream | PlayerEvent.TrackInfoUpdated);
-        }
-
-        public void Dispose ()
-        {
-            ServiceManager.PlaybackController.SourceChanged -= OnSourceChanged;
-            ServiceManager.SourceManager.ActiveSourceChanged -= OnActiveSourceChanged;
-            ServiceManager.PlayerEngine.DisconnectEvent (OnPlayerEvent);
-
-            if (pane != null) {
-                pane.Destroy ();
-                pane = null;
-            }
-
-            actions.Dispose ();
-        }
-
-        private void OnActiveSourceChanged (EventArgs args)
-        {
-            Banshee.Base.ThreadAssist.ProxyToMain (UpdateVisibility);
-        }
-
-        private void OnSourceChanged (object sender, EventArgs args)
-        {
-            Banshee.Base.ThreadAssist.ProxyToMain (UpdateVisibility);
-        }
-
-        private void OnPlayerEvent (PlayerEventArgs args)
-        {
-            TrackInfo track = ServiceManager.PlayerEngine.CurrentTrack;
-            if (track != null) {
-                ShowRecommendations (track.ArtistName);
-            }
-        }
-
-        private void UpdateVisibility ()
-        {
-            bool source_is_playback_source = (ServiceManager.SourceManager.ActiveSource as Banshee.Sources.ITrackModelSource) == ServiceManager.PlaybackController.Source;
-            pane.ShowWhenReady = ShowSchema.Get () && source_is_playback_source;
-            if (!source_is_playback_source) {
-                pane.HideWithTimeout ();
-            } else if (!pane.ShowWhenReady) {
-                pane.Hide ();
-            }
-        }
-
-        private void ShowRecommendations (string artist)
-        {
-            lock (this) {
-                if (pane.Visible && pane.Artist == artist) {
-                    return;
-                }
-
-                if (!String.IsNullOrEmpty (artist)) {
-                    pane.Artist = artist;
-                }
-
-                UpdateVisibility ();
-            }
-        }
-
-        public bool RecommendationsShown {
-            set {
-                ShowSchema.Set (value);
-
-                TrackInfo track = ServiceManager.PlayerEngine.CurrentTrack;
-                if (track != null) {
-                    pane.Artist = track.ArtistName;
-                }
-                UpdateVisibility ();
-            }
-        }
-
-        string IService.ServiceName {
-            get { return "LastfmRecommendationService"; }
-        }
-
-        public static readonly SchemaEntry<bool> ShowSchema = new SchemaEntry<bool>(
-            "plugins.recommendation", "show",
-            true,
-            "Show recommendations",
-            "Show recommendations for the currently playing artist"
-        );
-    }
-}
diff --git a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.addin.xml b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.addin.xml
index 8c55fd8..7577449 100644
--- a/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.addin.xml
+++ b/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.addin.xml
@@ -19,13 +19,13 @@
   <Extension path="/Banshee/SourceManager/Source">
     <Source class="Banshee.Lastfm.Radio.LastfmSource"/>
   </Extension>
+
+  <Extension path="/Banshee/ThickClient/ContextPage">
+    <ContextPage class="Banshee.Lastfm.Recommendations.ContextPage"/>
+  </Extension>
   
   <Extension path="/Banshee/ServiceManager/Service">
     <Service class="Banshee.Lastfm.Audioscrobbler.AudioscrobblerService"/>
   </Extension>
-
-  <Extension path="/Banshee/ServiceManager/Service">
-    <Service class="Banshee.Lastfm.Recommendations.RecommendationService"/>
-  </Extension>
   
 </Addin>
diff --git a/src/Extensions/Banshee.Lastfm/Makefile.am b/src/Extensions/Banshee.Lastfm/Makefile.am
index f1d86ea..00c459d 100644
--- a/src/Extensions/Banshee.Lastfm/Makefile.am
+++ b/src/Extensions/Banshee.Lastfm/Makefile.am
@@ -14,9 +14,8 @@ SOURCES =  \
 	Banshee.Lastfm.Radio/StationEditor.cs \
 	Banshee.Lastfm.Radio/StationSource.cs \
 	Banshee.Lastfm.Radio/StationType.cs \
-	Banshee.Lastfm.Recommendations/RecommendationActions.cs \
+	Banshee.Lastfm.Recommendations/ContextPage.cs \
 	Banshee.Lastfm.Recommendations/RecommendationPane.cs \
-	Banshee.Lastfm.Recommendations/RecommendationService.cs \
 	Banshee.Lastfm.Recommendations/SimilarArtistTile.cs
 
 
diff --git a/src/Extensions/Banshee.MediaWeb/Banshee.MediaWeb.addin.xml b/src/Extensions/Banshee.MediaWeb/Banshee.MediaWeb.addin.xml
index 5804823..d2bfda5 100644
--- a/src/Extensions/Banshee.MediaWeb/Banshee.MediaWeb.addin.xml
+++ b/src/Extensions/Banshee.MediaWeb/Banshee.MediaWeb.addin.xml
@@ -9,7 +9,7 @@
     description=""
     author=""
     url="http://banshee-project.org/";
-    defaultEnabled="true">
+    defaultEnabled="false">
 
   <Dependencies>
     <Addin id="Banshee.Services" version="1.0"/>
diff --git a/src/Libraries/Hyena/Hyena.csproj b/src/Libraries/Hyena/Hyena.csproj
index 392e5be..4b10ffc 100644
--- a/src/Libraries/Hyena/Hyena.csproj
+++ b/src/Libraries/Hyena/Hyena.csproj
@@ -140,6 +140,13 @@
     <Compile Include="Hyena.Data.Sqlite\HyenaSqliteArrayDataReader.cs" />
     <Compile Include="Hyena.Data.Sqlite\Tests\SqliteUtilTests.cs" />
     <Compile Include="Hyena.Query\ExactStringQueryValue.cs" />
+    <Compile Include="Hyena.Jobs\Job.cs" />
+    <Compile Include="Hyena.Jobs\JobExtensions.cs" />
+    <Compile Include="Hyena.Jobs\PriorityHints.cs" />
+    <Compile Include="Hyena.Jobs\Resource.cs" />
+    <Compile Include="Hyena.Jobs\Scheduler.cs" />
+    <Compile Include="Hyena.Jobs\SimpleAsyncJob.cs" />
+    <Compile Include="Hyena.Jobs\Tests\SchedulerTests.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>



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