Rhythmbox patch for Ratings in notification.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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