[rhythmbox] audioscrobbler: add popup menus to list items in profile source



commit 569af632c26a2a88cc0c5698704ec2fe169a0d6f
Author: Jamie Nicol <jamie thenicols net>
Date:   Wed Jun 30 12:38:33 2010 +0100

    audioscrobbler: add popup menus to list items in profile source
    
    Menus have option to view the item in a web browser

 .../rb-audioscrobbler-profile-source.c             |  313 ++++++++++++++------
 plugins/audioscrobbler/rb-audioscrobbler-user.c    |   21 +-
 plugins/audioscrobbler/rb-audioscrobbler-user.h    |    4 +-
 3 files changed, 225 insertions(+), 113 deletions(-)
---
diff --git a/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c b/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c
index aa2be7b..323eba2 100644
--- a/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c
+++ b/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c
@@ -85,6 +85,9 @@ struct _RBAudioscrobblerProfileSourcePrivate {
 	GtkWidget *top_artists_table;
 	GtkWidget *recommended_artists_area;
 	GtkWidget *recommended_artists_table;
+
+	GHashTable *button_to_popup_menu_map;
+	GHashTable *popup_menu_to_data_map;
 };
 
 #define RB_AUDIOSCROBBLER_PROFILE_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_AUDIOSCROBBLER_PROFILE_SOURCE, RBAudioscrobblerProfileSourcePrivate))
@@ -134,9 +137,6 @@ static void rb_audioscrobbler_profile_source_scrobbler_statistics_changed_cb (RB
 static void rb_audioscrobbler_profile_source_user_info_updated_cb (RBAudioscrobblerUser *user,
                                                                    RBAudioscrobblerUserData *info,
                                                                    gpointer user_data);
-static void rb_audioscrobbler_profile_source_set_user_list (RBAudioscrobblerProfileSource *source,
-                                                            GtkWidget *list_table,
-                                                            GPtrArray *list_data);
 static void rb_audioscrobbler_profile_source_recent_tracks_updated_cb (RBAudioscrobblerUser *user,
                                                                        GPtrArray *recent_tracks,
                                                                        gpointer user_data);
@@ -152,12 +152,25 @@ static void rb_audioscrobbler_profile_source_top_artists_updated_cb (RBAudioscro
 static void rb_audioscrobbler_profile_source_recommended_artists_updated_cb (RBAudioscrobblerUser *user,
                                                                              GPtrArray *recommended_artists,
                                                                              gpointer user_data);
+
+static void rb_audioscrobbler_profile_source_set_user_list (RBAudioscrobblerProfileSource *source,
+                                                            GtkWidget *list_table,
+                                                            GPtrArray *list_data);
+static GtkWidget *rb_audioscrobbler_profile_source_create_list_button (RBAudioscrobblerProfileSource *source,
+                                                                       RBAudioscrobblerUserData *data);
+static GtkWidget *rb_audioscrobbler_profile_source_create_popup_menu (RBAudioscrobblerProfileSource *source,
+                                                                      RBAudioscrobblerUserData *data);
+static void rb_audioscrobbler_profile_source_list_item_clicked_cb (GtkButton *button, gpointer user_data);
+static void rb_audioscrobbler_profile_source_list_item_view_url_activated_cb (GtkMenuItem *menuitem,
+                                                                              gpointer user_data);
 static void rb_audioscrobbler_profile_source_list_table_pack_start (GtkTable *list_table, GtkWidget *item);
 void rb_audioscrobbler_profile_source_list_layout_size_allocate_cb (GtkWidget *layout,
                                                                     GtkAllocation *allocation,
                                                                     gpointer user_data);
 
 
+
+
 enum {
 	PROP_0,
 	PROP_SERVICE
@@ -236,6 +249,9 @@ static void
 rb_audioscrobbler_profile_source_init (RBAudioscrobblerProfileSource *source)
 {
 	source->priv = RB_AUDIOSCROBBLER_PROFILE_SOURCE_GET_PRIVATE (source);
+
+	source->priv->button_to_popup_menu_map = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+	source->priv->popup_menu_to_data_map = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
@@ -358,6 +374,16 @@ rb_audioscrobbler_profile_source_dispose (GObject* object)
 		source->priv->scrobbling_enabled_notification_id = 0;
 	}
 
+	if (source->priv->button_to_popup_menu_map != NULL) {
+		g_hash_table_unref (source->priv->button_to_popup_menu_map);
+		source->priv->button_to_popup_menu_map = NULL;
+	}
+
+	if (source->priv->popup_menu_to_data_map != NULL) {
+		g_hash_table_unref (source->priv->popup_menu_to_data_map);
+		source->priv->popup_menu_to_data_map = NULL;
+	}
+
 	G_OBJECT_CLASS (rb_audioscrobbler_profile_source_parent_class)->dispose (object);
 }
 
@@ -697,100 +723,6 @@ rb_audioscrobbler_profile_source_scrobbler_statistics_changed_cb (RBAudioscrobbl
 }
 
 static void
-rb_audioscrobbler_profile_source_set_user_list (RBAudioscrobblerProfileSource *source,
-                                                GtkWidget *list_table,
-                                                GPtrArray *list_data)
-{
-	int i;
-	GList *button_node;
-
-	/* delete all existing buttons */
-	for (button_node = gtk_container_get_children (GTK_CONTAINER (list_table));
-	     button_node != NULL;
-	     button_node = g_list_next (button_node)) {
-		gtk_widget_destroy (button_node->data);
-	}
-
-	/* add a new button for each item in the list */
-	for (i = 0; i < list_data->len; i++) {
-		GtkWidget *button;
-		RBAudioscrobblerUserData *data;
-		char *button_text;
-		GtkWidget *label;
-		GtkWidget *label_alignment;
-		GtkWidget *button_contents;
-
-		button = gtk_button_new ();
-		gtk_button_set_alignment (GTK_BUTTON (button),
-			                  0, 0.5);
-		gtk_button_set_focus_on_click (GTK_BUTTON (button),
-			                       FALSE);
-		gtk_button_set_relief (GTK_BUTTON (button),
-			               GTK_RELIEF_NONE);
-
-		data = g_ptr_array_index (list_data, i);
-
-		button_text = NULL;
-		if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK) {
-			char *escaped_title_text;
-			char *escaped_artist_text;
-
-			escaped_title_text = g_markup_escape_text (data->track.title, -1);
-			escaped_artist_text = g_markup_escape_text (data->track.artist, -1);
-			button_text = g_strdup_printf ("%s\n<small>%s</small>",
-				                       escaped_title_text,
-				                       escaped_artist_text);
-
-			g_free (escaped_title_text);
-			g_free (escaped_artist_text);
-
-		} else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
-			button_text = g_markup_escape_text (data->artist.name, -1);
-		}
-
-		label = gtk_label_new ("");
-		gtk_label_set_markup (GTK_LABEL (label), button_text);
-		g_free (button_text);
-
-		label_alignment = gtk_alignment_new (0, 0.5, 0, 0);
-		gtk_container_add (GTK_CONTAINER (label_alignment), label);
-
-		button_contents = gtk_hbox_new (FALSE, 4);
-		if (data->image != NULL) {
-			GtkWidget *image;
-			GtkWidget *viewport;
-			GtkWidget *alignment;
-
-			image = gtk_image_new_from_pixbuf (data->image);
-
-			viewport = gtk_viewport_new (NULL, NULL);
-			gtk_container_add (GTK_CONTAINER (viewport), image);
-
-			alignment = gtk_alignment_new (0, 0.5, 0, 0);
-			gtk_container_add (GTK_CONTAINER (alignment), viewport);
-
-			gtk_box_pack_start (GTK_BOX (button_contents),
-			                    alignment,
-			                    FALSE, FALSE, 0);
-
-
-			gtk_alignment_set_padding (GTK_ALIGNMENT (label_alignment),
-			                           0, 0,
-			                           LIST_ITEM_IMAGE_SIZE - gdk_pixbuf_get_width (data->image), 0);
-		} else {
-			gtk_alignment_set_padding (GTK_ALIGNMENT (label_alignment), 0, 0, LIST_ITEM_IMAGE_SIZE + 4, 0);
-		}
-
-		gtk_box_pack_start (GTK_BOX (button_contents),
-		                    label_alignment,
-		                    FALSE, FALSE, 0);
-		gtk_container_add (GTK_CONTAINER (button), button_contents);
-
-		rb_audioscrobbler_profile_source_list_table_pack_start (GTK_TABLE (list_table), button);
-	}
-}
-
-static void
 rb_audioscrobbler_profile_source_user_info_updated_cb (RBAudioscrobblerUser *user,
                                                        RBAudioscrobblerUserData *data,
                                                        gpointer user_data)
@@ -807,7 +739,7 @@ rb_audioscrobbler_profile_source_user_info_updated_cb (RBAudioscrobblerUser *use
 		                     playcount_text);
 
 		gtk_link_button_set_uri (GTK_LINK_BUTTON (source->priv->view_profile_link),
-		                         data->user_info.url);
+		                         data->url);
 
 		gtk_image_set_from_pixbuf (GTK_IMAGE (source->priv->profile_image), data->image);
 
@@ -895,6 +827,191 @@ rb_audioscrobbler_profile_source_recommended_artists_updated_cb (RBAudioscrobble
 }
 
 static void
+rb_audioscrobbler_profile_source_set_user_list (RBAudioscrobblerProfileSource *source,
+                                                GtkWidget *list_table,
+                                                GPtrArray *list_data)
+{
+	int i;
+	GList *button_node;
+
+	/* delete all existing buttons */
+	for (button_node = gtk_container_get_children (GTK_CONTAINER (list_table));
+	     button_node != NULL;
+	     button_node = g_list_next (button_node)) {
+		GtkMenu *menu;
+		menu = g_hash_table_lookup (source->priv->button_to_popup_menu_map, button_node->data);
+		g_hash_table_remove (source->priv->button_to_popup_menu_map, button_node->data);
+		g_hash_table_remove (source->priv->popup_menu_to_data_map, menu);
+		gtk_widget_destroy (button_node->data);
+	}
+
+	/* add a new button for each item in the list */
+	for (i = 0; i < list_data->len; i++) {
+		RBAudioscrobblerUserData *data;
+		GtkWidget *button;
+		GtkWidget *menu;
+
+		data = g_ptr_array_index (list_data, i);
+		button = rb_audioscrobbler_profile_source_create_list_button (source, data);
+		menu = rb_audioscrobbler_profile_source_create_popup_menu (source, data);
+
+		g_hash_table_insert (source->priv->button_to_popup_menu_map, button, g_object_ref_sink (menu));
+		g_hash_table_insert (source->priv->popup_menu_to_data_map, menu, data);
+
+		rb_audioscrobbler_profile_source_list_table_pack_start (GTK_TABLE (list_table), button);
+	}
+}
+
+static GtkWidget *
+rb_audioscrobbler_profile_source_create_list_button (RBAudioscrobblerProfileSource *source,
+                                                     RBAudioscrobblerUserData *data)
+{
+	GtkWidget *button;
+	GtkWidget *button_contents;
+	char *button_text;
+	GtkWidget *label;
+	GtkWidget *label_alignment;
+
+	button = gtk_button_new ();
+	gtk_button_set_alignment (GTK_BUTTON (button),
+		                  0, 0.5);
+	gtk_button_set_focus_on_click (GTK_BUTTON (button),
+		                       FALSE);
+	gtk_button_set_relief (GTK_BUTTON (button),
+		               GTK_RELIEF_NONE);
+
+	button_contents = gtk_hbox_new (FALSE, 4);
+	gtk_container_add (GTK_CONTAINER (button), button_contents);
+
+	button_text = NULL;
+	if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK) {
+		char *escaped_title_text;
+		char *escaped_artist_text;
+
+		escaped_title_text = g_markup_escape_text (data->track.title, -1);
+		escaped_artist_text = g_markup_escape_text (data->track.artist, -1);
+		button_text = g_strdup_printf ("%s\n<small>%s</small>",
+			                       escaped_title_text,
+			                       escaped_artist_text);
+
+		g_free (escaped_title_text);
+		g_free (escaped_artist_text);
+
+	} else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
+		button_text = g_markup_escape_text (data->artist.name, -1);
+	}
+
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), button_text);
+	g_free (button_text);
+
+	label_alignment = gtk_alignment_new (0, 0.5, 0, 0);
+	gtk_container_add (GTK_CONTAINER (label_alignment), label);
+
+	if (data->image != NULL) {
+		GtkWidget *image;
+		GtkWidget *viewport;
+		GtkWidget *alignment;
+
+		image = gtk_image_new_from_pixbuf (data->image);
+
+		viewport = gtk_viewport_new (NULL, NULL);
+		gtk_container_add (GTK_CONTAINER (viewport), image);
+
+		alignment = gtk_alignment_new (0, 0.5, 0, 0);
+		gtk_container_add (GTK_CONTAINER (alignment), viewport);
+
+		gtk_box_pack_start (GTK_BOX (button_contents),
+		                    alignment,
+		                    FALSE, FALSE, 0);
+
+		gtk_alignment_set_padding (GTK_ALIGNMENT (label_alignment),
+		                           0, 0,
+		                           LIST_ITEM_IMAGE_SIZE - gdk_pixbuf_get_width (data->image), 0);
+	} else {
+		gtk_alignment_set_padding (GTK_ALIGNMENT (label_alignment), 0, 0, LIST_ITEM_IMAGE_SIZE + 4, 0);
+	}
+
+	gtk_box_pack_start (GTK_BOX (button_contents),
+	                    label_alignment,
+	                    FALSE, FALSE, 0);
+
+	g_signal_connect (button,
+		          "clicked",
+		          (GCallback) rb_audioscrobbler_profile_source_list_item_clicked_cb,
+		          source);
+
+	return button;
+}
+
+static GtkWidget *
+rb_audioscrobbler_profile_source_create_popup_menu (RBAudioscrobblerProfileSource *source,
+                                                    RBAudioscrobblerUserData *data)
+{
+	GtkWidget *menu;
+
+	menu = gtk_menu_new ();
+
+	if (data->url != NULL && data->url[0] != '\0') {
+		GtkWidget *view_url_item;
+		char *item_text;
+
+		item_text = g_strdup_printf (_("_View on %s"),
+		                             rb_audioscrobbler_service_get_name (source->priv->service));
+		view_url_item = gtk_menu_item_new_with_mnemonic (item_text);
+		g_signal_connect (view_url_item,
+				  "activate",
+				  (GCallback) rb_audioscrobbler_profile_source_list_item_view_url_activated_cb,
+				  source);
+
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu), view_url_item);
+		g_free (item_text);
+	}
+
+	gtk_widget_show_all (menu);
+
+	return menu;
+}
+
+static void
+rb_audioscrobbler_profile_source_list_item_clicked_cb (GtkButton *button, gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source;
+	GtkWidget *menu;
+
+	source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+	menu = g_hash_table_lookup (source->priv->button_to_popup_menu_map, button);
+
+	/* show menu if it has any items in it */
+	if (g_list_length (gtk_container_get_children (GTK_CONTAINER (menu))) != 0) {
+		gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_list_item_view_url_activated_cb (GtkMenuItem *menuitem,
+                                                                  gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source;
+	GtkWidget *menu;
+	RBAudioscrobblerUserData *data;
+
+	source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+	menu = gtk_widget_get_parent (GTK_WIDGET (menuitem));
+	data = g_hash_table_lookup (source->priv->popup_menu_to_data_map, menu);
+
+	/* some urls are given to us without the http:// prefix */
+	if (g_str_has_prefix (data->url, "http://";) == TRUE) {
+		gtk_show_uri (NULL, data->url, GDK_CURRENT_TIME, NULL);
+	} else {
+		char *url;
+		url = g_strdup_printf ("%s%s", "http://";, data->url);
+		gtk_show_uri (NULL, url, GDK_CURRENT_TIME, NULL);
+		g_free (url);
+	}
+}
+
+static void
 rb_audioscrobbler_profile_source_list_table_pack_start (GtkTable *list_table, GtkWidget *item)
 {
 	int num_columns;
diff --git a/plugins/audioscrobbler/rb-audioscrobbler-user.c b/plugins/audioscrobbler/rb-audioscrobbler-user.c
index c621611..fd8ffc8 100644
--- a/plugins/audioscrobbler/rb-audioscrobbler-user.c
+++ b/plugins/audioscrobbler/rb-audioscrobbler-user.c
@@ -47,21 +47,19 @@ rb_audioscrobbler_user_data_copy (RBAudioscrobblerUserData *data)
 	if (data->image != NULL) {
 		d->image = g_object_ref (data->image);
 	}
+	d->url = g_strdup (data->url);
 
 	switch (d->type) {
 	case RB_AUDIOSCROBBLER_USER_DATA_TYPE_USER_INFO:
 		d->user_info.username = g_strdup (data->user_info.username);
-		d->user_info.url = g_strdup (data->user_info.url);
 		d->user_info.playcount = g_strdup (data->user_info.playcount);
 		break;
 	case RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK:
 		d->track.title = g_strdup (data->track.title);
 		d->track.artist = g_strdup (data->track.artist);
-		d->track.url = g_strdup (data->track.url);
 		break;
 	case RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST:
 		d->artist.name = g_strdup (data->artist.name);
-		d->artist.url = g_strdup (data->artist.url);
 		break;
 	}
 
@@ -74,20 +72,19 @@ rb_audioscrobbler_user_data_free (RBAudioscrobblerUserData *data)
 	if (data->image != NULL) {
 		g_object_unref (data->image);
 	}
+	g_free (data->url);
+
 	switch (data->type) {
 	case RB_AUDIOSCROBBLER_USER_DATA_TYPE_USER_INFO:
 		g_free (data->user_info.username);
-		g_free (data->user_info.url);
 		g_free (data->user_info.playcount);
 		break;
 	case RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK:
 		g_free (data->track.title);
 		g_free (data->track.artist);
-		g_free (data->track.url);
 		break;
 	case RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST:
 		g_free (data->artist.name);
-		g_free (data->artist.url);
 		break;
 	}
 
@@ -679,8 +676,8 @@ rb_audioscrobbler_user_parse_user_info (RBAudioscrobblerUser *user, const char *
 		user_info = g_slice_new0 (RBAudioscrobblerUserData);
 		user_info->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_USER_INFO;
 		user_info->user_info.username = g_strdup (json_object_get_string_member (user_object, "name"));
-		user_info->user_info.url = g_strdup (json_object_get_string_member (user_object, "url"));
 		user_info->user_info.playcount = g_strdup (json_object_get_string_member (user_object, "playcount"));
+		user_info->url = g_strdup (json_object_get_string_member (user_object, "url"));
 
 		user_info->image = gdk_pixbuf_new_from_file_at_size (rb_audioscrobbler_user_calculate_cached_image_path (user, user_info),
 		                                                     USER_PROFILE_IMAGE_SIZE, -1, NULL);
@@ -817,7 +814,7 @@ rb_audioscrobbler_user_parse_recent_tracks (RBAudioscrobblerUser *user, const ch
 			track->track.title = g_strdup (json_object_get_string_member (track_object, "name"));
 			artist_object = json_object_get_object_member (track_object, "artist");
 			track->track.artist = g_strdup (json_object_get_string_member (artist_object, "#text"));
-			track->track.url = g_strdup (json_object_get_string_member (track_object, "url"));
+			track->url = g_strdup (json_object_get_string_member (track_object, "url"));
 
 			g_ptr_array_add (recent_tracks, track);
 
@@ -957,7 +954,7 @@ rb_audioscrobbler_user_parse_top_tracks (RBAudioscrobblerUser *user, const char
 			track->track.title = g_strdup (json_object_get_string_member (track_object, "name"));
 			artist_object = json_object_get_object_member (track_object, "artist");
 			track->track.artist = g_strdup (json_object_get_string_member (artist_object, "name"));
-			track->track.url = g_strdup (json_object_get_string_member (track_object, "url"));
+			track->url = g_strdup (json_object_get_string_member (track_object, "url"));
 
 			g_ptr_array_add (top_tracks, track);
 
@@ -1097,7 +1094,7 @@ rb_audioscrobbler_user_parse_loved_tracks (RBAudioscrobblerUser *user, const cha
 			track->track.title = g_strdup (json_object_get_string_member (track_object, "name"));
 			artist_object = json_object_get_object_member (track_object, "artist");
 			track->track.artist = g_strdup (json_object_get_string_member (artist_object, "name"));
-			track->track.url = g_strdup (json_object_get_string_member (track_object, "url"));
+			track->url = g_strdup (json_object_get_string_member (track_object, "url"));
 
 			g_ptr_array_add (loved_tracks, track);
 
@@ -1234,7 +1231,7 @@ rb_audioscrobbler_user_parse_top_artists (RBAudioscrobblerUser *user, const char
 			artist = g_slice_new0 (RBAudioscrobblerUserData);
 			artist->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST;
 			artist->artist.name = g_strdup (json_object_get_string_member (artist_object, "name"));
-			artist->artist.url = g_strdup (json_object_get_string_member (artist_object, "url"));
+			artist->url = g_strdup (json_object_get_string_member (artist_object, "url"));
 
 			g_ptr_array_add (top_artists, artist);
 
@@ -1389,7 +1386,7 @@ rb_audioscrobbler_user_parse_recommended_artists (RBAudioscrobblerUser *user, co
 				artist = g_slice_new0 (RBAudioscrobblerUserData);
 				artist->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST;
 				artist->artist.name = g_strdup (json_object_get_string_member (artist_object, "name"));
-				artist->artist.url = g_strdup (json_object_get_string_member (artist_object, "url"));
+				artist->url = g_strdup (json_object_get_string_member (artist_object, "url"));
 
 				g_ptr_array_add (recommended_artists, artist);
 
diff --git a/plugins/audioscrobbler/rb-audioscrobbler-user.h b/plugins/audioscrobbler/rb-audioscrobbler-user.h
index ad8b2bc..8aaa591 100644
--- a/plugins/audioscrobbler/rb-audioscrobbler-user.h
+++ b/plugins/audioscrobbler/rb-audioscrobbler-user.h
@@ -46,23 +46,21 @@ typedef struct {
 	} type;
 
 	GdkPixbuf *image;
+	char *url;
 
 	union {
 		struct {
 			char *username;
-			char *url;
 			char *playcount;
 		} user_info;
 
 		struct {
 			char *title;
 			char *artist;
-			char *url;
 		} track;
 
 		struct {
 			char *name;
-			char *url;
 		} artist;
 	};
 } RBAudioscrobblerUserData;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]