Hi all, I have been working on a patch for the Notification plugin to add rating buttons to the Notification. I wanted to put the idea as well as the patch up for an initial review.
The patch does the following: 1. Adds buttons to Rate Up and Rate Down the currently playing song in the Notification bubble. 2. Adds a Preference UI for the Notification plugin from where the ratings buttons can be enabled/disabled. 3. Adds schema for saving the setting. The buttons I am using for rating songs up and down are 'go-up' and 'go-down' stock icons for lack of a better choice. Nirbheek has pointed out this UI inconsistency already and I am open to suggestions of how to deal with it. I have posted screen-shots and a short video demonstration of the plugin on my blog here: http://musicallyut.blogspot.in/2012/03/rate-songs-in-notifications-with.html The patch is also available from here: https://gist.github.com/1978836 Thanks! ~ musically_ut
From 15b12ffc5b4b22ab104cf796e7173db4eeaefe90 Mon Sep 17 00:00:00 2001 From: Utkarsh Upadhyay <musically...@gmail.com> Date: Fri, 2 Mar 2012 11:56:32 +0530 Subject: [PATCH] Notification rating plugin. --- data/org.gnome.rhythmbox.gschema.xml | 10 + plugins/notification/Makefile.am | 6 + plugins/notification/notification-preferences.ui | 28 +++ plugins/notification/rb-notification-plugin.c | 243 ++++++++++++++++++++- 4 files changed, 275 insertions(+), 12 deletions(-) create mode 100644 plugins/notification/notification-preferences.ui diff --git a/data/org.gnome.rhythmbox.gschema.xml b/data/org.gnome.rhythmbox.gschema.xml index 4d6458c..457daa5 100644 --- a/data/org.gnome.rhythmbox.gschema.xml +++ b/data/org.gnome.rhythmbox.gschema.xml @@ -268,6 +268,16 @@ <child name='source' schema='org.gnome.rhythmbox.source'/> </schema> + <schema id="org.gnome.rhythmbox.plugins.notifications"> + <key name="ratings" type="b"> + <default>true</default> + <summary>Whether to show ratings button in notifications.</summary> + <description> + If true, the buttons to increase/decrease ratings will be displayed in the notifications. + </description> + </key> + </schema> + <schema id="org.gnome.rhythmbox.plugins.audioscrobbler.service"> <key name="enabled" type="b"> <default>true</default> diff --git a/plugins/notification/Makefile.am b/plugins/notification/Makefile.am index f6c3db1..c027af5 100644 --- a/plugins/notification/Makefile.am +++ b/plugins/notification/Makefile.am @@ -1,6 +1,7 @@ NULL = plugindir = $(PLUGINDIR)/notification +plugindatadir = $(PLUGINDATADIR)/notification plugin_LTLIBRARIES = libnotification.la libnotification_la_SOURCES = \ @@ -33,6 +34,10 @@ INCLUDES = \ $(NOTIFY_CFLAGS) \ -D_BSD_SOURCE +gtkbuilderdir = $(plugindatadir) +gtkbuilder_DATA = \ + notification-preferences.ui + plugin_in_files = notification.plugin.in %.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache @@ -47,6 +52,7 @@ plugin_DATA = \ EXTRA_DIST = \ $(plugin_in_files) \ + $(gtkbuilder_DATA) \ $(NULL) CLEANFILES = \ diff --git a/plugins/notification/notification-preferences.ui b/plugins/notification/notification-preferences.ui new file mode 100644 index 0000000..a0949c0 --- /dev/null +++ b/plugins/notification/notification-preferences.ui @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkVBox" id="config"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">start</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkCheckButton" id="rating_buttons_enabled_check"> + <property name="label" translatable="yes">Show rating buttons</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_action_appearance">False</property> + <property name="xalign">0</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> +</interface> diff --git a/plugins/notification/rb-notification-plugin.c b/plugins/notification/rb-notification-plugin.c index 0671b3d..ae8a14f 100644 --- a/plugins/notification/rb-notification-plugin.c +++ b/plugins/notification/rb-notification-plugin.c @@ -28,6 +28,12 @@ #include <config.h> +#include <libpeas-gtk/peas-gtk.h> + +#include <lib/rb-builder-helpers.h> +#include <lib/rb-file-helpers.h> +#include <plugins/rb-plugin-macros.h> + #include <string.h> #include <glib/gi18n-lib.h> #include <gtk/gtk.h> @@ -53,19 +59,30 @@ #define RB_IS_NOTIFICATION_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_NOTIFICATION_PLUGIN)) #define RB_NOTIFICATION_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_NOTIFICATION_PLUGIN, RBNotificationPluginClass)) +#define NOTIFICATION_SETTINGS_SCHEMA "org.gnome.rhythmbox.plugins.notifications" +#define NOTIFICATION_SETTINGS_PATH "/org/gnome/rhythmbox/plugins/notifications" +#define NOTIFICATION_RATING_ENABLED_KEY "ratings" + typedef struct { PeasExtensionBase parent; + GtkWidget *config_dialog; + /* current playing data */ char *current_title; - char *current_album_and_artist; /* from _album_ by _artist_ */ + char *current_album_artist_rating; /* from _album_ by _artist_ rated _rating_ */ + gdouble rating; gchar *notify_art_path; NotifyNotification *notification; gboolean notify_supports_actions; gboolean notify_supports_icon_buttons; gboolean notify_supports_persistence; + gboolean rating_buttons_enabled; + + GtkWidget *rating_buttons_enabled_check; + GSettings *notification_settings; RBShellPlayer *shell_player; RhythmDB *db; @@ -79,7 +96,14 @@ typedef struct G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module); -RB_DEFINE_PLUGIN(RB_TYPE_NOTIFICATION_PLUGIN, RBNotificationPlugin, rb_notification_plugin,) +static GtkWidget *impl_create_configure_widget (PeasGtkConfigurable *bplugin); +static void peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface); + +RB_DEFINE_PLUGIN(RB_TYPE_NOTIFICATION_PLUGIN, + RBNotificationPlugin, + rb_notification_plugin, + (G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_GTK_TYPE_CONFIGURABLE, + peas_gtk_configurable_iface_init))) static gchar * markup_escape (const char *text) @@ -113,6 +137,90 @@ notification_playpause_cb (NotifyNotification *notification, } static void +notification_ratedown_cb (NotifyNotification *notification, + const char *action, + RBNotificationPlugin *plugin) +{ + rb_debug ("notification action: %s", action); + + RhythmDBEntry *playing; + + playing = rb_shell_player_get_playing_entry (plugin->shell_player); + if (playing == NULL) { + rb_debug ("no song is playing!"); + return; + } + + gdouble rating = rhythmdb_entry_get_double (playing, RHYTHMDB_PROP_RATING); + + /* To keep with the increase button, we'll start from rating = 2 */ + if (rating == 0) { + rating = 2; + } else { + rating--; + } + + if (rating < 1) { + rating = 1; + } + + rb_debug ("new rating: %lf", rating); + + GValue value = { 0, }; + + g_value_init (&value, G_TYPE_DOUBLE); + g_value_set_double (&value, rating); + rhythmdb_entry_set (plugin->db, playing, RHYTHMDB_PROP_RATING, &value); + g_value_unset (&value); + + rhythmdb_commit (plugin->db); + + rhythmdb_entry_unref (playing); +} + +static void +notification_rateup_cb (NotifyNotification *notification, + const char *action, + RBNotificationPlugin *plugin) +{ + rb_debug ("notification action: %s", action); + + RhythmDBEntry *playing; + + playing = rb_shell_player_get_playing_entry (plugin->shell_player); + if (playing == NULL) { + rb_debug ("no song is playing!"); + return; + } + + gdouble rating = rhythmdb_entry_get_double (playing, RHYTHMDB_PROP_RATING); + + /* pushing the increase button 5 times is un-cool, so we'll start from rating = 4 */ + if (rating == 0) { + rating = 4; + } else { + rating++; + } + + if (rating > 5) { + rating = 5; + } + + rb_debug ("new rating: %lf", rating); + + GValue value = { 0, }; + + g_value_init (&value, G_TYPE_DOUBLE); + g_value_set_double (&value, rating); + rhythmdb_entry_set (plugin->db, playing, RHYTHMDB_PROP_RATING, &value); + g_value_unset (&value); + + rhythmdb_commit (plugin->db); + + rhythmdb_entry_unref (playing); +} + +static void notification_previous_cb (NotifyNotification *notification, const char *action, RBNotificationPlugin *plugin) @@ -122,6 +230,57 @@ notification_previous_cb (NotifyNotification *notification, } static void +notification_settings_changed_cb (GSettings *settings, + const char *key, + RBNotificationPlugin *plugin) { + + rb_debug ("notification setting changed for key: %s", key); + if (g_strcmp0 (key, NOTIFICATION_RATING_ENABLED_KEY) != 0) { + return; + } + + plugin->rating_buttons_enabled = g_settings_get_boolean (settings, key); + rb_debug ("ratings buttons are now: %s", + plugin->rating_buttons_enabled ? "enabled" : "disabled"); +} + +static GtkWidget * +impl_create_configure_widget (PeasGtkConfigurable *bplugin) +{ + RBNotificationPlugin *plugin; + char *builderfile; + GtkBuilder *builder; + GtkWidget *widget; + + rb_debug("initializing configuration widget"); + plugin = RB_NOTIFICATION_PLUGIN (bplugin); + + builderfile = rb_find_plugin_data_file (G_OBJECT (plugin), "notification-preferences.ui"); + if (builderfile == NULL) { + g_warning ("can't find notification-preferences.ui"); + return NULL; + } + + builder = rb_builder_load (builderfile, plugin); + g_free (builderfile); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "config")); + g_object_ref_sink (widget); + + plugin->rating_buttons_enabled_check = GTK_WIDGET (gtk_builder_get_object (builder, "rating_buttons_enabled_check")); + g_settings_bind (plugin->notification_settings, NOTIFICATION_RATING_ENABLED_KEY, plugin->rating_buttons_enabled_check, "active", G_SETTINGS_BIND_DEFAULT); + g_object_unref (builder); + return widget; +} + +static void +peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface) +{ + rb_debug("setting initializer function"); + iface->create_configure_widget = impl_create_configure_widget; +} + +static void do_notify (RBNotificationPlugin *plugin, guint timeout, const char *primary, @@ -217,6 +376,23 @@ do_notify (RBNotificationPlugin *plugin, (NotifyActionCallback) notification_playpause_cb, plugin, NULL); + + if (plugin->rating_buttons_enabled) { + notify_notification_add_action (notification, + "go-down", + _("Rating Down"), + (NotifyActionCallback) notification_ratedown_cb, + plugin, + NULL); + notify_notification_add_action (notification, + "go-up", + _("Rating Up"), + (NotifyActionCallback) notification_rateup_cb, + plugin, + NULL); + + } + notify_notification_set_hint (notification, "action-icons", g_variant_new_boolean (TRUE)); } @@ -251,7 +427,7 @@ notify_playing_entry (RBNotificationPlugin *plugin, gboolean requested) do_notify (plugin, PLAYING_ENTRY_NOTIFY_TIME * 1000, plugin->current_title, - plugin->current_album_and_artist, + plugin->current_album_artist_rating, plugin->notify_art_path, TRUE); } @@ -300,8 +476,10 @@ shell_notify_custom_cb (RBShell *shell, static void get_artist_album_templates (const char *artist, const char *album, + const char *rating, const char **artist_template, - const char **album_template) + const char **album_template, + const char **rating_template) { PangoDirection tag_dir; PangoDirection template_dir; @@ -310,6 +488,8 @@ get_artist_album_templates (const char *artist, *artist_template = _("by <i>%s</i>"); /* Translators: from Album */ *album_template = _("from <i>%s</i>"); + /* Translators: rated Rating*/ + *rating_template = _("rated <i>%s</i>"); /* find the direction (left-to-right or right-to-left) of the * track's tags and the localized templates @@ -320,6 +500,9 @@ get_artist_album_templates (const char *artist, } else if (album != NULL && album[0] != '\0') { tag_dir = pango_find_base_dir (album, -1); template_dir = pango_find_base_dir (*album_template, -1); + } else if (rating != 0) { + tag_dir = pango_find_base_dir (rating, -1); + template_dir = pango_find_base_dir (*rating_template, -1); } else { return; } @@ -338,6 +521,7 @@ get_artist_album_templates (const char *artist, */ *artist_template = "<i>%s</i>"; *album_template = "/ <i>%s</i>"; + *rating_template = "/ (<i>%s</i>)"; } } @@ -373,26 +557,30 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry) char *artist = NULL; char *album = NULL; char *title = NULL; + char rating_str[10]; + gdouble rating; GString *secondary; RBExtDBKey *key; const char *artist_template = NULL; const char *album_template = NULL; + const char *rating_template = NULL; g_free (plugin->current_title); - g_free (plugin->current_album_and_artist); + g_free (plugin->current_album_artist_rating); g_free (plugin->notify_art_path); plugin->current_title = NULL; - plugin->current_album_and_artist = NULL; + plugin->current_album_artist_rating = NULL; + plugin->rating = 0; plugin->notify_art_path = NULL; if (entry == NULL) { plugin->current_title = g_strdup (_("Not Playing")); - plugin->current_album_and_artist = g_strdup (""); + plugin->current_album_artist_rating = g_strdup (""); return; } - secondary = g_string_sized_new (100); + secondary = g_string_sized_new (150); /* request album art */ key = rhythmdb_entry_create_ext_db_key (entry, RHYTHMDB_PROP_ALBUM); @@ -427,7 +615,11 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry) album = markup_escape (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM)); } - get_artist_album_templates (artist, album, &artist_template, &album_template); + /* get ratings */ + rating = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_RATING); + sprintf (rating_str, "%d", (int) rating); + + get_artist_album_templates (artist, album, rating_str, &artist_template, &album_template, &rating_template); if (artist != NULL && artist[0] != '\0') { g_string_append_printf (secondary, artist_template, artist); @@ -442,6 +634,14 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry) } g_free (album); + if (rating != 0) { + if (secondary->len != 0) + g_string_append_c (secondary, ' '); + + g_string_append_printf (secondary, rating_template, rating_str); + } + + /* get title and possibly stream name. * if we have a streaming song title, the entry's title * property is the stream name. @@ -475,8 +675,9 @@ update_current_playing_data (RBNotificationPlugin *plugin, RhythmDBEntry *entry) title = g_strdup (_("Unknown")); } + plugin->rating = rating; plugin->current_title = title; - plugin->current_album_and_artist = g_string_free (secondary, FALSE); + plugin->current_album_artist_rating = g_string_free (secondary, FALSE); } static void @@ -547,6 +748,12 @@ impl_activate (PeasActivatable *bplugin) G_CALLBACK (db_stream_metadata_cb), plugin, 0); g_signal_connect_object (plugin->db, "entry_extra_metadata_notify::" RHYTHMDB_PROP_STREAM_SONG_ALBUM, G_CALLBACK (db_stream_metadata_cb), plugin, 0); + g_signal_connect_object (plugin->notification_settings, + "changed", + G_CALLBACK (notification_settings_changed_cb), + plugin, 0); + + notification_settings_changed_cb (plugin->notification_settings, NOTIFICATION_RATING_ENABLED_KEY, plugin); plugin->art_store = rb_ext_db_new ("album-art"); @@ -563,6 +770,11 @@ impl_deactivate (PeasActivatable *bplugin) plugin = RB_NOTIFICATION_PLUGIN (bplugin); + if (plugin->notification_settings != NULL) { + g_object_unref (plugin->notification_settings); + plugin->notification_settings = NULL; + } + g_object_get (plugin, "object", &shell, NULL); cleanup_notification (plugin); @@ -590,10 +802,10 @@ impl_deactivate (PeasActivatable *bplugin) /* forget what's playing */ g_free (plugin->current_title); - g_free (plugin->current_album_and_artist); + g_free (plugin->current_album_artist_rating); g_free (plugin->notify_art_path); plugin->current_title = NULL; - plugin->current_album_and_artist = NULL; + plugin->current_album_artist_rating = NULL; plugin->notify_art_path = NULL; g_object_unref (shell); @@ -602,6 +814,10 @@ impl_deactivate (PeasActivatable *bplugin) static void rb_notification_plugin_init (RBNotificationPlugin *plugin) { + rb_debug("RBNotificationPlugin initialising"); + + plugin->notification_settings = g_settings_new_with_path (NOTIFICATION_SETTINGS_SCHEMA, + NOTIFICATION_SETTINGS_PATH "/Ratings"); } G_MODULE_EXPORT void @@ -611,4 +827,7 @@ peas_register_types (PeasObjectModule *module) peas_object_module_register_extension_type (module, PEAS_TYPE_ACTIVATABLE, RB_TYPE_NOTIFICATION_PLUGIN); + peas_object_module_register_extension_type (module, + PEAS_GTK_TYPE_CONFIGURABLE, + RB_TYPE_NOTIFICATION_PLUGIN); } -- 1.7.7.6
_______________________________________________ rhythmbox-devel mailing list rhythmbox-devel@gnome.org http://mail.gnome.org/mailman/listinfo/rhythmbox-devel