[banshee] Add the SoundMenu extension from the community extensions



commit dcca3c742bb8b2d50f89341401f2a158de402166
Author: Bertrand Lorentz <bertrand lorentz gmail com>
Date:   Sat Dec 11 14:18:49 2010 +0100

    Add the SoundMenu extension from the community extensions
    
    This extension integrates Banshee in the sound menu/indicator available
    in Ubuntu.
    
    It is disabled by default and can be enabled by passing
    --enable-soundmenu to configure.
    
    It depends on indicate-sharp to register itself with the indicator, but
    this dep will probably be removed in the near future, as the
    registration process is reworked.
    
    The original source history is available in the Banshee Community
    Extension repository :
    http://gitorious.org/banshee-community-extensions

 Banshee.sln                                        |   13 +
 build/build.environment.mk                         |    2 +
 build/m4/banshee/soundmenu.m4                      |   23 +
 configure.ac                                       |    5 +
 data/addin-xml-strings.cs                          |    5 +
 po/POTFILES.in                                     |    1 +
 .../Banshee.SoundMenu/Banshee.SoundMenu.addin.xml  |   24 ++
 .../Banshee.SoundMenu/Banshee.SoundMenu.csproj     |   94 +++++
 .../Banshee.SoundMenu/SoundMenuService.cs          |  432 ++++++++++++++++++++
 src/Extensions/Banshee.SoundMenu/Makefile.am       |   21 +
 src/Extensions/Makefile.am                         |    1 +
 11 files changed, 621 insertions(+), 0 deletions(-)
---
diff --git a/Banshee.sln b/Banshee.sln
index ad28ba7..a1b9261 100644
--- a/Banshee.sln
+++ b/Banshee.sln
@@ -140,6 +140,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.AmazonMp3", "src\Ex
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.AmazonMp3.Store", "src\Extensions\Banshee.AmazonMp3.Store\Banshee.AmazonMp3.Store.csproj", "{AF8A9C6D-2188-413D-8EB8-C5E242BD68AC}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.SoundMenu", "src\Extensions\Banshee.SoundMenu\Banshee.SoundMenu.csproj", "{EE91126B-5500-4719-9FAA-36CBAC95DEE3}"
+EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}"
 	ProjectSection(SolutionItems) = postProject
 	EndProjectSection
@@ -438,6 +440,16 @@ Global
 		{EABA3019-7539-4430-9935-D36CEA96F250}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{EABA3019-7539-4430-9935-D36CEA96F250}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
 		{EABA3019-7539-4430-9935-D36CEA96F250}.Windows|Any CPU.Build.0 = Windows|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Debug|x86.Build.0 = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Release|Any CPU.Build.0 = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Release|x86.ActiveCfg = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Release|x86.Build.0 = Debug|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3}.Windows|Any CPU.Build.0 = Windows|Any CPU
 		{F38B53BA-8F85-4DC6-9B94-029C1CF96F24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{F38B53BA-8F85-4DC6-9B94-029C1CF96F24}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F38B53BA-8F85-4DC6-9B94-029C1CF96F24}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
@@ -515,6 +527,7 @@ Global
 		{8FF8A538-7D48-4AEB-A5C0-CA295DC1FE8D} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
 		{930ADBFD-07F1-4044-A1C5-8B28BD23DB5C} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
 		{AF8A9C6D-2188-413D-8EB8-C5E242BD68AC} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
+		{EE91126B-5500-4719-9FAA-36CBAC95DEE3} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
 		{95374549-9553-4C1E-9D89-667755F90E12} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
 		{95374549-9553-4C1E-9D89-667755F90E13} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
 		{C856EFD8-E812-4E61-8B76-E3583D94C233} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
diff --git a/build/build.environment.mk b/build/build.environment.mk
index dfd6e7b..137a8c6 100644
--- a/build/build.environment.mk
+++ b/build/build.environment.mk
@@ -28,6 +28,7 @@ LINK_GDATA = $(GDATASHARP_LIBS)
 LINK_MONOTORRENT_DBUS = $(MONOTORRENT_DBUS_LIBS)
 LINK_MONO_ADDINS_DEPS = $(MONO_ADDINS_LIBS)
 LINK_MONO_ADDINS_SETUP_DEPS = $(MONO_ADDINS_SETUP_LIBS)
+LINK_INDICATESHARP = $(INDICATESHARP_LIBS)
 
 DIR_BIN = $(top_builddir)/bin
 
@@ -147,6 +148,7 @@ REF_EXTENSION_PLAYQUEUE = $(LINK_BANSHEE_THICKCLIENT_DEPS)
 LINK_EXTENSION_PLAYQUEUE = -r:$(DIR_BIN)/Banshee.PlayQueue.dll
 LINK_EXTENSION_PLAYQUEUE_DEPS = $(REF_EXTENSION_PLAYQUEUE) \
 	$(LINK_EXTENSION_PLAYQUEUE)
+REF_EXTENSION_SOUNDMENU = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_INDICATESHARP)
 REF_EXTENSION_LASTFM = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_MONO_MEDIA) $(LINK_LASTFM) $(LINK_LASTFM_GUI)
 LINK_EXTENSION_LASTFM = -r:$(DIR_BIN)/Banshee.Lastfm.dll
 REF_EXTENSION_LASTFM_STREAMING = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_MONO_MEDIA) $(LINK_LASTFM) $(LINK_LASTFM_GUI) $(LINK_EXTENSION_LASTFM)
diff --git a/build/m4/banshee/soundmenu.m4 b/build/m4/banshee/soundmenu.m4
new file mode 100644
index 0000000..2b9c56b
--- /dev/null
+++ b/build/m4/banshee/soundmenu.m4
@@ -0,0 +1,23 @@
+AC_DEFUN([BANSHEE_CHECK_SOUNDMENU],
+[
+	LIBINDICATESHARP_REQUIRED=0.4.1
+
+	AC_ARG_ENABLE([soundmenu],
+		AS_HELP_STRING([--enable-soundmenu]
+                                [Enable sound menu support]),
+		enable_soundmenu=$enableval, enable_soundmenu=no
+	)
+
+	if test "x$enable_soundmenu" = "xyes"; then
+		has_indicatesharp=no
+		PKG_CHECK_MODULES(INDICATESHARP,
+			indicate-sharp-0.1 >= $LIBINDICATESHARP_REQUIRED,
+			has_indicatesharp=yes, has_indicatesharp=no)
+		if test "x$has_indicatesharp" = "xno"; then
+			AC_MSG_ERROR([indicate-sharp was not found or is not up to date. Please install indicate-sharp of at least version $LIBINDICATESHARP_REQUIRED, or disable sound menu support by passing --disable-soundmenu])
+		fi
+	fi
+
+	AM_CONDITIONAL(ENABLE_SOUNDMENU, test "x$enable_soundmenu" = "xyes")
+])
+
diff --git a/configure.ac b/configure.ac
index 4e1f898..0fd6ba0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -175,6 +175,9 @@ BANSHEE_CHECK_TORRENT
 dnl MeeGo integration (optional)
 BANSHEE_CHECK_MEEGO
 
+dnl Sound menu integration (optional)
+BANSHEE_CHECK_SOUNDMENU
+
 dnl Gapless, if we have a new-enough playbin2
 ENABLE_GAPLESS="no"
 AC_ARG_ENABLE(gapless-playback,
@@ -357,6 +360,7 @@ src/Extensions/Banshee.NowPlaying/Makefile
 src/Extensions/Banshee.PlayQueue/Makefile
 src/Extensions/Banshee.PlayerMigration/Makefile
 src/Extensions/Banshee.Podcasting/Makefile
+src/Extensions/Banshee.SoundMenu/Makefile
 src/Extensions/Banshee.SqlDebugConsole/Makefile
 src/Extensions/Banshee.Sample/Makefile
 src/Extensions/Banshee.Torrent/Makefile
@@ -413,6 +417,7 @@ ${PACKAGE}-${VERSION}
     Podcasts:          ${enable_podcast}
     Gapless playback:  ${ENABLE_GAPLESS} (gstreamer-plugins-base > 0.10.25.2)
     YouTube extension: ${enable_youtube} (gdata-sharp >= 1.4)
+    Sound menu:        ${enable_soundmenu} (indicate-sharp >= 0.4.1)
 
   Build/Development:
     Unit Tests:        ${do_tests} (nunit >= ${NUNIT_REQUIRED})
diff --git a/data/addin-xml-strings.cs b/data/addin-xml-strings.cs
index f264e9e..8246b86 100644
--- a/data/addin-xml-strings.cs
+++ b/data/addin-xml-strings.cs
@@ -203,6 +203,11 @@ internal static class AddinXmlStringCatalog
         Catalog.GetString (@"A sample source useful to developers.");
         Catalog.GetString (@"Sources");
 
+        // ../src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu.addin.xml
+        Catalog.GetString (@"Sound Menu integration for Banshee");
+        Catalog.GetString (@"Control Banshee from the Sound Menu.");
+        Catalog.GetString (@"Utilities");
+
         // ../src/Extensions/Banshee.SqlDebugConsole/Banshee.SqlDebugConsole.addin.xml
         Catalog.GetString (@"SQL Debug Console");
         Catalog.GetString (@"Provides a UI for monitoring Banshee's SQL usage, and running queries.");
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6356490..5b04eff 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -295,6 +295,7 @@ src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog/P
 src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastManager/Dialog/SyncPreferenceComboBox.cs
 src/Extensions/Banshee.RemoteAudio/Banshee.RemoteAudio/RemoteSpeakersWidget.cs
 src/Extensions/Banshee.Sample/Banshee.Sample/SampleSource.cs
+src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu/SoundMenuService.cs
 src/Extensions/Banshee.SqlDebugConsole/Banshee.SqlDebugConsole/SqlActions.cs
 src/Extensions/Banshee.Torrent/Banshee.Torrent.addin.xml
 src/Extensions/Banshee.Wikipedia/Banshee.Wikipedia.addin.xml
diff --git a/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu.addin.xml b/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu.addin.xml
new file mode 100644
index 0000000..c773618
--- /dev/null
+++ b/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu.addin.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Addin
+    id="Banshee.SoundMenu"
+    version="1.0"
+    compatVersion="1.0"
+    copyright="Copyright 2010 Bertrand Lorentz. Licensed under the MIT X11 license."
+    name="Sound Menu integration for Banshee"
+    category="Utilities"
+    description="Control Banshee from the Sound Menu."
+    author="Bertrand Lorentz"
+    url="http://banshee-project.org/";
+    defaultEnabled="false">
+
+  <Dependencies>
+    <Addin id="Banshee.Services" version="1.0"/>
+    <Addin id="Banshee.ThickClient" version="1.0"/>
+    <Addin id="Banshee.Mpris" version="1.0"/>
+  </Dependencies>
+
+  <Extension path="/Banshee/ServiceManager/Service">
+    <Service class="Banshee.SoundMenu.SoundMenuService"/>
+  </Extension>
+
+</Addin>
diff --git a/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu.csproj b/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu.csproj
new file mode 100644
index 0000000..3c43de7
--- /dev/null
+++ b/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu.csproj
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; ToolsVersion="3.5">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.50727</ProductVersion>
+    <ProjectGuid>{EE91126B-5500-4719-9FAA-36CBAC95DEE3}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <UseParentDirectoryAsNamespace>true</UseParentDirectoryAsNamespace>
+    <AssemblyName>Banshee.SoundMenu</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <Optimize>true</Optimize>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+    <RootNamespace>SoundMenu</RootNamespace>
+    <AssemblyOriginatorKeyFile>.</AssemblyOriginatorKeyFile>
+    <ReleaseVersion>1.3</ReleaseVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <OutputPath>..\..\..\bin</OutputPath>
+    <WarningLevel>4</WarningLevel>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Windows|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <OutputPath>..\..\..\bin</OutputPath>
+    <PlatformTarget>x86</PlatformTarget>
+    <WarningLevel>4</WarningLevel>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+      <Package>gtk-sharp-2.0</Package>
+    </Reference>
+    <Reference Include="Mono.Posix" />
+    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+      <Package>gtk-sharp-2.0</Package>
+    </Reference>
+    <Reference Include="Mono.Addins, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756">
+      <Package>mono-addins</Package>
+    </Reference>
+    <Reference Include="indicate-sharp, Version=0.4.1.0, Culture=neutral, PublicKeyToken=2e8e49ba7d172cb0">
+      <Package>indicate-sharp-0.1</Package>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Banshee.SoundMenu.addin.xml">
+      <LogicalName>Banshee.SoundMenu.addin.xml</LogicalName>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Banshee.SoundMenu\SoundMenuService.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ProjectExtensions>
+    <MonoDevelop>
+      <Properties>
+        <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="Makefile.am">
+          <BuildFilesVar Sync="true" Name="SOURCES" />
+          <DeployFilesVar />
+          <ResourcesVar Sync="true" Name="RESOURCES" />
+          <OthersVar />
+          <GacRefVar />
+          <AsmRefVar />
+          <ProjectRefVar />
+        </MonoDevelop.Autotools.MakefileInfo>
+      </Properties>
+    </MonoDevelop>
+  </ProjectExtensions>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Banshee.Services\Banshee.Services.csproj">
+      <Project>{B28354F0-BA87-44E8-989F-B864A3C7C09F}</Project>
+      <Name>Banshee.Services</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Core\Banshee.ThickClient\Banshee.ThickClient.csproj">
+      <Project>{AC839523-7BDF-4AB6-8115-E17921B96EC6}</Project>
+      <Name>Banshee.ThickClient</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Hyena\Hyena\Hyena.csproj">
+      <Project>{95374549-9553-4C1E-9D89-667755F90E12}</Project>
+      <Name>Hyena</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Core\Banshee.Core\Banshee.Core.csproj">
+      <Project>{2ADB831A-A050-47D0-B6B9-9C19D60233BB}</Project>
+      <Name>Banshee.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+</Project>
diff --git a/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu/SoundMenuService.cs b/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu/SoundMenuService.cs
new file mode 100644
index 0000000..cae9b52
--- /dev/null
+++ b/src/Extensions/Banshee.SoundMenu/Banshee.SoundMenu/SoundMenuService.cs
@@ -0,0 +1,432 @@
+//
+// SoundMenuService.cs
+//
+// Authors:
+//   Bertrand Lorentz <bertrand lorentz gmail com>
+//
+// Copyright (C) 2010 Bertrand Lorentz
+//
+// 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.Addins;
+using Mono.Unix;
+using Gtk;
+using Notifications;
+
+using Hyena;
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.Collection.Gui;
+using Banshee.Configuration;
+using Banshee.Gui;
+using Banshee.IO;
+using Banshee.MediaEngine;
+using Banshee.ServiceStack;
+using Banshee.Preferences;
+
+using Indicate;
+
+namespace Banshee.SoundMenu
+{
+    public class SoundMenuService : IExtensionService
+    {
+        private bool? actions_supported;
+        private ArtworkManager artwork_manager_service;
+        private Notification current_nf;
+        private TrackInfo current_track;
+        private GtkElementsService elements_service;
+        private InterfaceActionService interface_action_service;
+        private string notify_last_artist;
+        private string notify_last_title;
+        private Server server;
+
+        private const int icon_size = 42;
+
+        public SoundMenuService ()
+        {
+        }
+
+        void IExtensionService.Initialize ()
+        {
+            elements_service = ServiceManager.Get<GtkElementsService> ();
+            interface_action_service = ServiceManager.Get<InterfaceActionService> ();
+
+            var notif_addin = AddinManager.Registry.GetAddin("Banshee.NotificationArea");
+            var ind_addin = AddinManager.Registry.GetAddin("Banshee.AppIndicator");
+
+            if (notif_addin != null && notif_addin.Enabled) {
+                Log.Debug("NotificationArea conflicts with SoundMenu, disabling NotificationArea");
+                notif_addin.Enabled = false;
+            } else if (ind_addin != null && ind_addin.Enabled) {
+                Log.Debug("AppIndicator conflicts with SoundMenu, disabling AppIndicator");
+                ind_addin.Enabled = false;
+            }
+
+            AddinManager.AddinLoaded += OnAddinLoaded;
+
+            if (!ServiceStartup ()) {
+                ServiceManager.ServiceStarted += OnServiceStarted;
+            }
+        }
+
+        private void OnServiceStarted (ServiceStartedArgs args)
+        {
+            if (args.Service is Banshee.Gui.InterfaceActionService) {
+                interface_action_service = (InterfaceActionService)args.Service;
+            } else if (args.Service is GtkElementsService) {
+                elements_service = (GtkElementsService)args.Service;
+            }
+
+            ServiceStartup ();
+        }
+
+        private bool ServiceStartup ()
+        {
+            if (elements_service == null || interface_action_service == null) {
+                return false;
+            }
+
+            interface_action_service.GlobalActions.Add (new ActionEntry [] {
+                new ActionEntry ("CloseAction", Stock.Close,
+                    Catalog.GetString ("_Close"), "<Control>W",
+                    Catalog.GetString ("Close"), CloseWindow)
+            });
+
+
+            InstallPreferences ();
+            server = Server.RefDefault ();
+            if (Enabled) {
+                Register ();
+            }
+
+            ServiceManager.PlayerEngine.ConnectEvent (OnPlayerEvent,
+               PlayerEvent.StartOfStream |
+               PlayerEvent.EndOfStream |
+               PlayerEvent.TrackInfoUpdated |
+               PlayerEvent.StateChange);
+
+            artwork_manager_service = ServiceManager.Get<ArtworkManager> ();
+            artwork_manager_service.AddCachedSize (icon_size);
+
+            RegisterCloseHandler ();
+
+            ServiceManager.ServiceStarted -= OnServiceStarted;
+
+            return true;
+        }
+
+        public void Dispose ()
+        {
+            if (current_nf != null) {
+                try {
+                    current_nf.Close ();
+                } catch {}
+            }
+
+            UninstallPreferences ();
+
+            ServiceManager.PlayerEngine.DisconnectEvent (OnPlayerEvent);
+
+            elements_service.PrimaryWindowClose = null;
+
+            Gtk.Action close_action = interface_action_service.GlobalActions["CloseAction"];
+            if (close_action != null) {
+                interface_action_service.GlobalActions.Remove (close_action);
+            }
+
+            AddinManager.AddinLoaded -= OnAddinLoaded;
+
+            elements_service = null;
+            interface_action_service = null;
+        }
+
+        void OnAddinLoaded (object sender, AddinEventArgs args)
+        {
+            if (args.AddinId == "Banshee.NotificationArea" || args.AddinId == "Banshee.AppIndicator") {
+                Log.Debug("SoundMenu conflicts with " + args.AddinId + ", disabling SoundMenu");
+                AddinManager.Registry.GetAddin("Banshee.SoundMenu").Enabled = false;
+            }
+        }
+
+        public void Register ()
+        {
+            Log.Debug ("Registering with sound indicator");
+            server.SetType ("music.banshee");
+            string desktop_file = Paths.Combine (Paths.InstalledApplicationDataRoot,
+                                                 "applications", "banshee-1.desktop");
+            server.DesktopFile (desktop_file);
+            server.Show ();
+        }
+
+        public void Unregister ()
+        {
+            server.Hide ();
+        }
+
+#region Notifications
+        private bool ActionsSupported {
+            get {
+                if (!actions_supported.HasValue) {
+                    actions_supported = Notifications.Global.Capabilities != null &&
+                        Array.IndexOf (Notifications.Global.Capabilities, "actions") > -1;
+                }
+
+                return actions_supported.Value;
+            }
+        }
+
+        private bool OnPrimaryWindowClose ()
+        {
+            CloseWindow (null, null);
+            return true;
+        }
+
+        private void CloseWindow (object o, EventArgs args)
+        {
+            try {
+                if (NotifyOnCloseSchema.Get ()) {
+                    Notification nf = new Notification (
+                        Catalog.GetString ("Still Running"),
+                        Catalog.GetString (
+                            "Banshee was closed to the sound menu. " +
+                            "Use the <i>Quit</i> option to end your session."),
+                            "media-player-banshee");
+                    nf.Urgency = Urgency.Low;
+                    nf.Show ();
+
+                    NotifyOnCloseSchema.Set (false);
+                }
+            } catch (Exception e) {
+                Log.Warning ("Error while trying to notify of window close.", e.Message, false);
+            }
+
+            elements_service.PrimaryWindow.Visible = false;
+        }
+
+        private void OnPlayerEvent (PlayerEventArgs args)
+        {
+            switch (args.Event) {
+                case PlayerEvent.StartOfStream:
+                case PlayerEvent.TrackInfoUpdated:
+                    current_track = ServiceManager.PlayerEngine.CurrentTrack;
+                    ShowTrackNotification ();
+                    break;
+                case PlayerEvent.EndOfStream:
+                    current_track = null;
+                    break;
+            }
+        }
+
+        private void OnSongSkipped (object o, ActionArgs args)
+        {
+            if (args.Action == "skip-song") {
+                ServiceManager.PlaybackController.Next ();
+            }
+        }
+
+        private string GetByFrom (string artist, string display_artist, string album, string display_album)
+        {
+            bool has_artist = !String.IsNullOrEmpty (artist);
+            bool has_album = !String.IsNullOrEmpty (album);
+
+            string markup = null;
+            if (has_artist && has_album) {
+                // Translators: {0} and {1} are Artist Name and
+                // Album Title, respectively;
+                // e.g. 'by Parkway Drive from Killing with a Smile'
+                markup = String.Format (Catalog.GetString ("by '{0}' from '{1}'"),
+                                        display_artist, display_album);
+            } else if (has_album) {
+                // Translators: {0} is for Album Title;
+                // e.g. 'from Killing with a Smile'
+                markup = String.Format (Catalog.GetString ("from '{0}'"),
+                                        display_album);
+            } else {
+                // Translators: {0} is for Artist Name;
+                // e.g. 'by Parkway Drive'
+                markup = String.Format(Catalog.GetString ("by '{0}'"),
+                                       display_artist);
+            }
+            return markup;
+        }
+
+        private void ShowTrackNotification ()
+        {
+            // This has to happen before the next if, otherwise the last_* members aren't set correctly.
+            if (current_track == null || (notify_last_title == current_track.DisplayTrackTitle
+                && notify_last_artist == current_track.DisplayArtistName)) {
+                return;
+            }
+
+            notify_last_title = current_track.DisplayTrackTitle;
+            notify_last_artist = current_track.DisplayArtistName;
+
+            if (!ShowNotificationsSchema.Get ()) {
+                return;
+            }
+
+            foreach (var window in elements_service.ContentWindows) {
+                if (window.HasToplevelFocus) {
+                    return;
+                }
+            }
+
+            bool is_notification_daemon = false;
+            try {
+                var name = Notifications.Global.ServerInformation.Name;
+                is_notification_daemon = name == "notification-daemon" || name == "Notification Daemon";
+            } catch {
+                // This will be reached if no notification daemon is running
+                return;
+            }
+
+            string message = GetByFrom (
+                current_track.ArtistName, current_track.DisplayArtistName,
+                current_track.AlbumTitle, current_track.DisplayAlbumTitle);
+
+            string image = null;
+
+            image = is_notification_daemon
+                ? CoverArtSpec.GetPathForSize (current_track.ArtworkId, icon_size)
+                : CoverArtSpec.GetPath (current_track.ArtworkId);
+
+            if (!File.Exists (new SafeUri(image))) {
+                if (artwork_manager_service != null) {
+                    // artwork does not exist, try looking up the pixbuf to trigger scaling or conversion
+                    Gdk.Pixbuf tmp_pixbuf = is_notification_daemon
+                        ? artwork_manager_service.LookupScalePixbuf (current_track.ArtworkId, icon_size)
+                        : artwork_manager_service.LookupPixbuf (current_track.ArtworkId);
+
+                    if (tmp_pixbuf == null) {
+                        image = "audio-x-generic";
+                    } else {
+                        tmp_pixbuf.Dispose ();
+                    }
+                }
+            }
+            try {
+                if (current_nf == null) {
+                    current_nf = new Notification (current_track.DisplayTrackTitle, message, image);
+                } else {
+                    current_nf.Summary = current_track.DisplayTrackTitle;
+                    current_nf.Body = message;
+                    current_nf.IconName = image;
+                }
+
+                current_nf.Urgency = Urgency.Low;
+                current_nf.Timeout = 4500;
+
+                if (!current_track.IsLive && ActionsSupported && interface_action_service.PlaybackActions["NextAction"].Sensitive) {
+                    current_nf.AddAction ("skip-song", Catalog.GetString ("Skip this item"), OnSongSkipped);
+                }
+                current_nf.Show ();
+
+            } catch (Exception e) {
+                Log.Warning ("Cannot show notification", e.Message, false);
+            }
+        }
+
+        private void RegisterCloseHandler ()
+        {
+            if (elements_service.PrimaryWindowClose == null) {
+                elements_service.PrimaryWindowClose = OnPrimaryWindowClose;
+            }
+        }
+
+        private void UnregisterCloseHandler ()
+        {
+            if (elements_service.PrimaryWindowClose != null) {
+                elements_service.PrimaryWindowClose = null;
+            }
+        }
+#endregion
+
+#region Preferences
+        private PreferenceBase enabled_pref;
+
+        private void InstallPreferences ()
+        {
+            PreferenceService service = ServiceManager.Get<PreferenceService> ();
+            if (service == null) {
+                return;
+            }
+
+            enabled_pref = service["general"]["misc"].Add (
+                new SchemaPreference<bool> (EnabledSchema,
+                    Catalog.GetString ("_Show Banshee in the sound menu"),
+                    Catalog.GetString ("Control Banshee through the sound menu"),
+                    delegate { Enabled = EnabledSchema.Get (); })
+            );
+        }
+
+        private void UninstallPreferences ()
+        {
+            PreferenceService service = ServiceManager.Get<PreferenceService> ();
+            if (service == null) {
+                return;
+            }
+
+            service["general"]["misc"].Remove (enabled_pref);
+        }
+
+        public bool Enabled {
+            get { return EnabledSchema.Get (); }
+            set {
+                EnabledSchema.Set (value);
+                if (value) {
+                    Register ();
+                    RegisterCloseHandler ();
+                } else {
+                    Unregister ();
+                    UnregisterCloseHandler ();
+                }
+            }
+        }
+
+        private static readonly SchemaEntry<bool> EnabledSchema = new SchemaEntry<bool> (
+            "plugins.soundmenu", "enabled",
+            true,
+            "Show Banshee in the sound menu",
+            "Show Banshee in the sound menu"
+        );
+
+        public static readonly SchemaEntry<bool> ShowNotificationsSchema = new SchemaEntry<bool> (
+            "plugins.soundmenu", "show_notifications",
+            true,
+            "Show notifications",
+            "Show information notifications when item starts playing"
+        );
+
+        public static readonly SchemaEntry<bool> NotifyOnCloseSchema = new SchemaEntry<bool> (
+            "plugins.soundmenu", "notify_on_close",
+            true,
+            "Show a notification when closing main window",
+            "When the main window is closed, show a notification stating this has happened."
+        );
+#endregion
+
+        string IService.ServiceName {
+            get { return "SoundMenuService"; }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.SoundMenu/Makefile.am b/src/Extensions/Banshee.SoundMenu/Makefile.am
new file mode 100644
index 0000000..2ec47d4
--- /dev/null
+++ b/src/Extensions/Banshee.SoundMenu/Makefile.am
@@ -0,0 +1,21 @@
+if EXTERNAL_NOTIFY_SHARP
+NOTIFY_SHARP_LIBS = $(NOTIFY_SHARP_LIBS)
+else
+GMCS_FLAGS+="-define:INTERNAL_NOTIFY_SHARP"
+NOTIFY_SHARP_LIBS = 
+endif
+
+ASSEMBLY = Banshee.SoundMenu
+TARGET = library
+LINK = $(REF_EXTENSION_SOUNDMENU) $(NOTIFY_SHARP_LIBS)
+INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
+
+SOURCES = Banshee.SoundMenu/SoundMenuService.cs
+
+RESOURCES = Banshee.SoundMenu.addin.xml
+
+if ENABLE_SOUNDMENU
+include $(top_srcdir)/build/build.mk
+else
+EXTRA_DIST = $(SOURCES) $(RESOURCES)
+endif
diff --git a/src/Extensions/Makefile.am b/src/Extensions/Makefile.am
index 3ccce53..5a270a5 100644
--- a/src/Extensions/Makefile.am
+++ b/src/Extensions/Makefile.am
@@ -26,6 +26,7 @@ SUBDIRS = \
 	Banshee.MeeGo \
 	Banshee.Podcasting \
 	Banshee.Sample \
+	Banshee.SoundMenu \
 	Banshee.SqlDebugConsole \
 	Banshee.Torrent \
 	Banshee.RemoteAudio \



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