Skip to content

Instantly share code, notes, and snippets.

@musically-ut
Created March 17, 2012 08:09
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save musically-ut/2056511 to your computer and use it in GitHub Desktop.
Rhythmbox patch for Ratings in notification.
From 8a07f968d5185e23ec089669b1d2e2c65ccdcf68 Mon Sep 17 00:00:00 2001
From: Utkarsh Upadhyay <musically.ut@gmail.com>
Date: Sat, 17 Mar 2012 13:14:45 +0530
Subject: [PATCH] Ratings in notifications.
1. Adds buttons to rate the playing entry from the song-change/status-icon notification itself.
2. Adds configuration UI to enable/disable the rating buttons.
---
data/org.gnome.rhythmbox.gschema.xml | 10 +
plugins/notification/Makefile.am | 6 +
plugins/notification/notification-preferences.ui | 28 +++
plugins/notification/rb-notification-plugin.c | 274 +++++++++++++++++++++-
4 files changed, 306 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 5a1207c..d66f79e 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..66430b3 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,31 @@
#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;
+ gboolean rating_changed;
+
+ GtkWidget *rating_buttons_enabled_check;
+ GSettings *notification_settings;
RBShellPlayer *shell_player;
RhythmDB *db;
@@ -79,7 +97,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 +138,94 @@ notification_playpause_cb (NotifyNotification *notification,
}
static void
+notification_ratedown_cb (NotifyNotification *notification,
+ const char *action,
+ RBNotificationPlugin *plugin)
+{
+ rb_debug ("notification action: %s", action);
+
+ plugin->rating_changed = TRUE;
+
+ 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);
+
+ plugin->rating_changed = TRUE;
+
+ 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 +235,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 +381,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 +432,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 +481,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 +493,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 +505,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 +526,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 +562,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 +620,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 +639,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 +680,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
@@ -505,6 +711,29 @@ is_playing_entry (RBNotificationPlugin *plugin, RhythmDBEntry *entry)
return (entry == playing);
}
+static void
+entry_changed_cb (RhythmDB *db, RhythmDBEntry *entry, GValueArray *changes, RBNotificationPlugin *plugin) {
+ RhythmDBEntry *playing_entry = rb_shell_player_get_playing_entry (plugin->shell_player);
+ if (playing_entry == NULL) {
+ return;
+ }
+
+ if (playing_entry == entry) {
+ int i;
+ for (i = 0; i < changes->n_values; i++) {
+ RhythmDBEntryChange *change = g_value_get_boxed (g_value_array_get_nth (changes, i));
+ if (change->prop == RHYTHMDB_PROP_RATING && plugin->rating_changed) {
+ // Ratings were changed for the playing entry,
+ // reshow notification if the change was done
+ // via a button press in the notification
+ plugin->rating_changed = FALSE;
+ update_current_playing_data (plugin, entry);
+ notify_playing_entry (plugin, FALSE);
+ }
+ }
+ }
+}
+
static void
db_stream_metadata_cb (RhythmDB *db,
RhythmDBEntry *entry,
@@ -547,8 +776,16 @@ 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->db, "entry-changed", G_CALLBACK (entry_changed_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");
+ plugin->rating_changed = FALSE;
/* hook into shell preferences so we can poke stuff into the general prefs page? */
@@ -563,6 +800,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);
@@ -577,6 +819,7 @@ impl_deactivate (PeasActivatable *bplugin)
if (plugin->db != NULL) {
g_signal_handlers_disconnect_by_func (plugin->db, db_stream_metadata_cb, plugin);
+ g_signal_handlers_disconnect_by_func (plugin->db, entry_changed_cb, plugin);
g_object_unref (plugin->db);
plugin->db = NULL;
@@ -590,10 +833,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 +845,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 +858,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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment