Ratings in notifications.

Utkarsh Upadhyay <[email protected]>
Newsgroups gmane.comp.gnome.apps.rhythmbox.devel
Message-ID <CALh3q9xLvnCGBoB2LBhFB8GcfhK4PqVwFuW8UgvgBB3GNsQUcw@mail.gmail.com>
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

_______________________________________________
rhythmbox-devel mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/rhythmbox-devel
0001-Notification-rating-plugin.patch (text/x-patch, 17.8 KB)
From 15b12ffc5b4b22ab104cf796e7173db4eeaefe90 Mon Sep 17 00:00:00 2001
From: Utkarsh Upadhyay <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.