From 70aba9fd30e3c03ea9e084e3dd5799d8e686f98f Mon Sep 17 00:00:00 2001 From: Sense Hofstede Date: Tue, 16 Feb 2010 22:22:13 +0100 Subject: [PATCH] Adding (optional) support for Indicator Application For its upcoming release 'Ubuntu 10.04 Lucid Lynx' Ubuntu is pushing a new kind of systray: Indicator Application. This follows the KNotify specification as proposed at FreeDesktop. A big advantage of the new systray is greater consistency and better themeability. Details about the project can be found at . GNOME bug #607800 The NotificationPlaceholder was removed from src/Extensions/Banshee.NotificationArea/Resources/NotificationAreaMenu.xml since it makes the Indicator Application look ugly and its removal doesn't seem to affect the regular GtkStatusIcon tray icon. Now NotoficationAreaService.ShowTrackNotification() first checkes whether notif_area.Widget() is not null before passing it on. To make up for the lack of tooltips we show the title and the artist of the song currently being played in the systray menu. It is possible to disable the Application Indicator with an option accessible via Preferences. --- build/m4/banshee/indicator-application.m4 | 29 +++ configure.ac | 58 ++++--- .../IndicatorApplicationNotificationAreaBox.cs | 119 +++++++++++++ .../NotificationAreaService.cs | 180 ++++++++++++++++++-- .../Banshee.NotificationArea/Makefile.am | 7 +- .../Resources/NotificationAreaMenu.xml | 2 - 6 files changed, 352 insertions(+), 43 deletions(-) create mode 100644 build/m4/banshee/indicator-application.m4 create mode 100644 src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/IndicatorApplicationNotificationAreaBox.cs Index: banshee-1.5.3/build/m4/banshee/indicator-application.m4 =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ banshee-1.5.3/build/m4/banshee/indicator-application.m4 2010-03-13 11:30:13.410416181 +0100 @@ -0,0 +1,29 @@ +AC_DEFUN([BANSHEE_CHECK_INDICATOR_APPLICATION], +[ + APPINDICATOR_REQUIRED=0.0.16 + + AC_ARG_ENABLE(appindicator, + AS_HELP_STRING([--enable-appindicator[=@<:@no/auto/yes@:>@]],[Build support for application indicators ]), + [enable_appindicator=$enableval], + [enable_appindicator="no"]) + + if test x$enable_appindicator = xauto ; then + PKG_CHECK_EXISTS([appindicator-sharp-0.1 >= $APPINDICATOR_REQUIRED], + enable_appindicator="yes", + enable_appindicator="no") + fi + + if test x$enable_appindicator = xyes ; then + PKG_CHECK_EXISTS([appindicator-sharp-0.1 >= $APPINDICATOR_REQUIRED],, + AC_MSG_ERROR([appindicator-sharp-0.1 is not installed])) + PKG_CHECK_MODULES(APPINDICATOR, + appindicator-sharp-0.1 >= $APPINDICATOR_REQUIRED) + AC_SUBST(APPINDICATOR_CFLAGS) + AC_SUBST(APPINDICATOR_LIBS) + AM_CONDITIONAL(HAVE_APPINDICATOR, true) + AC_DEFINE(HAVE_APPINDICATOR, 1, [Have AppIndicator]) + fi + AM_CONDITIONAL(HAVE_APPINDICATOR, test x"$enable_appindicator" = xyes) +]) + + Index: banshee-1.5.3/configure.ac =================================================================== --- banshee-1.5.3.orig/configure.ac 2010-03-13 11:28:24.000000000 +0100 +++ banshee-1.5.3/configure.ac 2010-03-13 11:33:21.180415818 +0100 @@ -89,6 +89,9 @@ dnl Mono.Addins libraries BANSHEE_CHECK_MONO_ADDINS +dnl Indicator Application +BANSHEE_CHECK_INDICATOR_APPLICATION + dnl notify-sharp library BANSHEE_CHECK_NOTIFY_SHARP @@ -346,6 +349,7 @@ Builtin Equalizer: ${enable_builtin_equalizer} Library Watcher: ${HAVE_MONO_2_4_3} (requires Mono >= 2.4.3) GIO Support: ${enable_gio} (requires glib >= 2.22, gtk-sharp-beans, and gio-sharp) + Indicator Application: ${enable_appindicator} Build/Development: Unit Tests: ${do_tests} (requires nunit >= ${NUNIT_REQUIRED}) Index: banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/IndicatorApplicationNotificationAreaBox.cs =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/IndicatorApplicationNotificationAreaBox.cs 2010-03-13 11:30:13.410416181 +0100 @@ -0,0 +1,119 @@ +// +// IndicatorApplicationNotificationAreaBox.cs +// +// Author: +// Sense Hofstede +// +// 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. +// + +#if HAVE_APPINDICATOR + +using System; +using Mono.Unix; +using AppIndicator; +using Gtk; + +using Banshee.Gui; +using Banshee.ServiceStack; +using Banshee.Collection; +using Banshee.MediaEngine; + +namespace Banshee.NotificationArea +{ + public class IndicatorApplicationNotificationAreaBox : INotificationAreaBox + { + + protected ApplicationIndicator indicator; + + public Menu Menu { + get { return indicator.Menu; } + set { indicator.Menu = value; } + } + + public event EventHandler Activated; + public event EventHandler Disconnected; + public event PopupMenuHandler PopupMenuEvent; + + public void PositionMenu (Menu menu, out int x, out int y, out bool push_in) + { + // Assigning meaningless value to comply to the interface + x = 0; + y = 0; + push_in = false; + } + + public Widget Widget { + get { return null; } + } + + public IndicatorApplicationNotificationAreaBox () + { + + indicator = new ApplicationIndicator ("banshee", + Banshee.ServiceStack.Application.IconName, + Category.ApplicationStatus); + + // Hide the icon until it's told to show itself + indicator.Status = Status.Passive; + + } + + public void OnPlayerEvent (PlayerEventArgs args) + { + // Our lifeline to the NotificationAreaService, + // connecting here because we can't do so in the constructor + NotificationAreaService notification_service = ServiceManager.Get (); + + switch (args.Event) { + case PlayerEvent.StartOfStream: + case PlayerEvent.TrackInfoUpdated: + TrackInfo track_info = ServiceManager.PlayerEngine.CurrentTrack; + notification_service.artist_menu_action.Label = track_info.DisplayArtistName; + notification_service.song_menu_action.Label = track_info.DisplayTrackTitle; + notification_service.song_menu_action.Visible = true; + break; + case PlayerEvent.EndOfStream: + notification_service.artist_menu_action.Label = Catalog.GetString ("Not Playing"); + notification_service.song_menu_action.Visible = false; + break; + } + } + + public void Show () + { + indicator.Status = Status.Active; + } + + public void Hide () + { + indicator.Status = Status.Passive; + } + + public void Dispose () + { + // Remove the indicator + indicator.Dispose(); + } + } +} + +#endif + Index: banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs =================================================================== --- banshee-1.5.3.orig/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs 2009-12-15 22:41:45.000000000 +0100 +++ banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs 2010-03-13 11:30:13.410416181 +0100 @@ -38,6 +38,7 @@ using Banshee.Collection; using Banshee.Collection.Database; using Banshee.ServiceStack; +using Banshee.Preferences; using Banshee.Configuration; using Banshee.Gui; using Banshee.Collection.Gui; @@ -51,6 +52,7 @@ public class NotificationAreaService : IExtensionService { private INotificationAreaBox notif_area; + private string notif_type; private GtkElementsService elements_service; private InterfaceActionService interface_action_service; private ArtworkManager artwork_manager_service; @@ -58,6 +60,11 @@ private Menu menu; private RatingMenuItem rating_menu_item; + #if HAVE_APPINDICATOR + private CheckMenuItem show_menu_item; + public Gtk.Action artist_menu_action; + public Gtk.Action song_menu_action; + #endif private BansheeActionGroup actions; private bool? actions_supported; @@ -123,6 +130,10 @@ private void Initialize () { + #if HAVE_APPINDICATOR + InstallPreferences (); + #endif + interface_action_service.GlobalActions.Add (new ActionEntry [] { new ActionEntry ("CloseAction", Stock.Close, Catalog.GetString ("_Close"), "W", @@ -136,6 +147,18 @@ Catalog.GetString ("Show notifications when item changes"), ToggleNotifications, ShowNotifications) }); + #if HAVE_APPINDICATOR + artist_menu_action = new Gtk.Action ("ArtistMenuAction", Catalog.GetString ("Not Playing")); + artist_menu_action.Sensitive = false; + artist_menu_action.Visible = true; + song_menu_action = new Gtk.Action ("SongMenuAction", Catalog.GetString ("Not Playing")); + song_menu_action.Sensitive = false; + song_menu_action.Visible = false; + + actions.Add (artist_menu_action); + actions.Add (song_menu_action); + #endif + interface_action_service.AddActionGroup (actions); ui_manager_id = (int)interface_action_service.UIManager.AddUiFromResource ("NotificationAreaMenu.xml"); @@ -184,6 +207,10 @@ ui_manager_id = -1; } + #if HAVE_APPINDICATOR + UninstallPreferences (); + #endif + actions = null; elements_service = null; interface_action_service = null; @@ -204,9 +231,29 @@ private bool BuildNotificationArea () { + #if HAVE_APPINDICATOR + if (UseAppIndicatorSchema.Get() == true) { + try { + notif_area = new IndicatorApplicationNotificationAreaBox (); + notif_type = "IndicatorApplication"; + + BuildContextMenu (); + + var indicator = notif_area as IndicatorApplicationNotificationAreaBox; + if (indicator != null) { + indicator.Menu = menu; + } + } catch { + } + } + + if (Environment.OSVersion.Platform == PlatformID.Unix && notif_area == null) { + #else if (Environment.OSVersion.Platform == PlatformID.Unix) { + #endif try { notif_area = new X11NotificationAreaBox (); + notif_type = "X11"; } catch { } } @@ -214,6 +261,7 @@ if (notif_area == null) { #if HAVE_GTK_2_10 notif_area = new GtkNotificationAreaBox (elements_service.PrimaryWindow); + notif_type = "Gtk"; #endif } @@ -221,24 +269,52 @@ return false; } - notif_area.Disconnected += OnNotificationAreaDisconnected; - notif_area.Activated += OnNotificationAreaActivated; - notif_area.PopupMenuEvent += OnNotificationAreaPopupMenuEvent; + if (notif_type != "IndicatorApplication") { + notif_area.Disconnected += OnNotificationAreaDisconnected; + notif_area.Activated += OnNotificationAreaActivated; + notif_area.PopupMenuEvent += OnNotificationAreaPopupMenuEvent; + } if (!QuitOnCloseSchema.Get ()) { RegisterCloseHandler (); } + Hyena.Log.Debug (String.Format ("Now using the NotificationArea with the driver: {0}", notif_type)); + return true; } + public void ReloadNotificationArea () + { + Hyena.Log.Debug (String.Format ("Reloading the notification area, the old driver was {0}.", notif_type)); + + DisposeNotificationArea (); + notif_area.Dispose(); + notif_area = null; + notif_type = null; + + menu = null; + + if (!BuildNotificationArea ()) { + Hyena.Log.Warning ("Error when trying to reload the notification area.", false); + Dispose (); + return; + } + + notif_area.Show (); + } + private void DisposeNotificationArea () { - if (notif_area != null) { + if (notif_area != null && notif_type != "IndicatorApplication") { notif_area.Disconnected -= OnNotificationAreaDisconnected; notif_area.Activated -= OnNotificationAreaActivated; notif_area.PopupMenuEvent -= OnNotificationAreaPopupMenuEvent; } + + if (!QuitOnCloseSchema.Get ()) { + UnregisterCloseHandler (); + } } private void BuildContextMenu () @@ -250,6 +326,16 @@ menu = (Menu)interface_action_service.UIManager.GetWidget("/NotificationAreaIconMenu"); menu.Show (); + if (notif_type == "IndicatorApplication") { + Widget artist_menu_item = artist_menu_action.CreateMenuItem (); + + Widget song_menu_item = song_menu_action.CreateMenuItem (); + + menu.Prepend (new SeparatorMenuItem ()); + menu.Prepend (song_menu_item); + menu.Prepend (artist_menu_item); + } + for (int i = 0; i < menu.Children.Length; i++) { if (menu.Children[i].Name == "Previous") { int j = i; @@ -268,11 +354,19 @@ if (j != i) { menu.Insert (new SeparatorMenuItem (), i++ + 2); } - - rating_menu_item = new RatingMenuItem (); - rating_menu_item.Activated += OnRatingChanged; - ToggleRatingMenuSensitive (); - menu.Insert (rating_menu_item, i + 2); + if (notif_type != "IndicatorApplication") { + rating_menu_item = new RatingMenuItem (); + rating_menu_item.Activated += OnRatingChanged; + ToggleRatingMenuSensitive (); + menu.Insert (rating_menu_item, i + 2); + break; + } + } else if (menu.Children[i].Name == "ToggleNotifications" && notif_type == "IndicatorApplication") { + show_menu_item = new CheckMenuItem (Catalog.GetString ("Show Banshee")); + show_menu_item.Toggled += OnShowWindowToggled; + show_menu_item.Active = true; + show_menu_item.Show(); + menu.Insert (show_menu_item, i + 2); break; } } @@ -298,6 +392,11 @@ return true; } + private void OnShowWindowToggled(object o, EventArgs args) + { + elements_service.PrimaryWindow.SetVisible( show_menu_item.Active ); + } + private void OnNotificationAreaDisconnected (object o, EventArgs args) { // Ensure we don't keep the destroyed reference around @@ -351,6 +450,9 @@ } elements_service.PrimaryWindow.SetVisible (false); + if (notif_type == "IndicatorApplication") { + show_menu_item.Active = false; + } } private void ToggleNotifications (object o, EventArgs args) @@ -456,13 +558,20 @@ try { if (current_nf == null) { - current_nf = new Notification (current_track.DisplayTrackTitle, - message, image, notif_area.Widget); + if (notif_area.Widget != null) { + current_nf = new Notification (current_track.DisplayTrackTitle, + message, image, notif_area.Widget); + } else { + current_nf = new Notification (current_track.DisplayTrackTitle, + message, image); + } } else { current_nf.Summary = current_track.DisplayTrackTitle; current_nf.Body = message; current_nf.Icon = image; - current_nf.AttachToWidget (notif_area.Widget); + if (notif_area.Widget != null) { + current_nf.AttachToWidget (notif_area.Widget); + } } current_nf.Urgency = Urgency.Low; current_nf.Timeout = 4500; @@ -477,7 +586,7 @@ private Cairo.Color TextLightColor { get { - return Hyena.Gui.Theming.GtkTheme.GetCairoTextMidColor (notif_area.Widget); + return Hyena.Gui.Theming.GtkTheme.GetCairoTextMidColor (notif_area.Widget ?? elements_service.PrimaryWindow); } } @@ -551,6 +660,42 @@ } } } + #if HAVE_APPINDICATOR + private PreferenceBase use_app_indicator_preference; + + private void InstallPreferences () + { + + PreferenceService service = ServiceManager.Get (); + if (service == null) { + return; + } + + use_app_indicator_preference = service["general"]["misc"].Add (new SchemaPreference (UseAppIndicatorSchema, + Catalog.GetString ("_Use an Application Indicator"), + Catalog.GetString ("Use an Application Indicator instead of a regular system tray icon."), + delegate { + try { + NotificationAreaService notification_service = ServiceManager.Get (); + notification_service.ReloadNotificationArea(); + } catch { + Hyena.Log.Warning ("You tried to change a setting of the Banshee.NotificationArea, but the extension isn't enabled."); + } + } + )); + } + + private void UninstallPreferences () + { + PreferenceService service = ServiceManager.Get (); + if (service == null) { + return; + } + + service["general"]["misc"].Remove (use_app_indicator_preference); + use_app_indicator_preference = null; + } + #endif string IService.ServiceName { get { return "NotificationAreaService"; } @@ -583,5 +728,14 @@ "Quit on close", "Quit instead of hide to notification area on close" ); + + #if HAVE_APPINDICATOR + public static readonly SchemaEntry UseAppIndicatorSchema = new SchemaEntry ( + "plugins.notification_area", "use_app_indicator", + true, + "Use an Application Indicator", + "Use an Application Indicator instead of a regular system tray icon." + ); + #endif } } Index: banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Makefile.am =================================================================== --- banshee-1.5.3.orig/src/Extensions/Banshee.NotificationArea/Makefile.am 2010-01-15 20:50:48.000000000 +0100 +++ banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Makefile.am 2010-03-13 11:30:13.410416181 +0100 @@ -7,13 +7,18 @@ NOTIFY_SHARP_LIBS = endif +if HAVE_APPINDICATOR +BUILD_DEFINES = "-define:HAVE_APPINDICATOR" +endif + ASSEMBLY = Banshee.NotificationArea TARGET = library -LINK = $(REF_EXTENSION_NOTIFICATIONAREA) $(NOTIFY_SHARP_LIBS) +LINK = $(REF_EXTENSION_NOTIFICATIONAREA) $(NOTIFY_SHARP_LIBS) $(APPINDICATOR_LIBS) INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR) SOURCES = \ Banshee.NotificationArea/GtkNotificationAreaBox.cs \ + Banshee.NotificationArea/IndicatorApplicationNotificationAreaBox.cs \ Banshee.NotificationArea/INotificationAreaBox.cs \ Banshee.NotificationArea/NotificationAreaService.cs \ Banshee.NotificationArea/TrackInfoPopup.cs \ Index: banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Resources/NotificationAreaMenu.xml =================================================================== --- banshee-1.5.3.orig/src/Extensions/Banshee.NotificationArea/Resources/NotificationAreaMenu.xml 2009-04-24 21:41:02.000000000 +0200 +++ banshee-1.5.3/src/Extensions/Banshee.NotificationArea/Resources/NotificationAreaMenu.xml 2010-03-13 11:30:13.410416181 +0100 @@ -5,8 +5,6 @@ - -