No issues found
1 /*
2 * rb-audioscrobbler-profile-page.c
3 *
4 * Copyright (C) 2010 Jamie Nicol <jamie@thenicols.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * The Rhythmbox authors hereby grant permission for non-GPL compatible
12 * GStreamer plugins to be used and distributed together with GStreamer
13 * and Rhythmbox. This permission is above and beyond the permissions granted
14 * by the GPL license by which Rhythmbox is covered. If you modify this code
15 * you may extend this exception to your version of the code, but you are not
16 * obligated to do so. If you do not wish to do so, delete this exception
17 * statement from your version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #include <glib/gi18n.h>
34 #include <gtk/gtk.h>
35 #include <json-glib/json-glib.h>
36 #include <math.h>
37
38 #include <lib/rb-debug.h>
39 #include <lib/rb-builder-helpers.h>
40 #include <lib/rb-file-helpers.h>
41 #include <lib/rb-util.h>
42 #include <sources/rb-display-page-tree.h>
43 #include <sources/rb-display-page-group.h>
44 #include <widgets/eggwrapbox.h>
45 #include <widgets/rb-source-toolbar.h>
46
47 #include "rb-audioscrobbler-profile-page.h"
48 #include "rb-audioscrobbler.h"
49 #include "rb-audioscrobbler-account.h"
50 #include "rb-audioscrobbler-user.h"
51 #include "rb-audioscrobbler-radio-source.h"
52 #include "rb-audioscrobbler-radio-track-entry-type.h"
53
54 #define AUDIOSCROBBLER_PROFILE_PAGE_POPUP_PATH "/AudioscrobblerProfilePagePopup"
55
56 struct _RBAudioscrobblerProfilePagePrivate {
57 RBAudioscrobblerService *service;
58 RBAudioscrobblerAccount *account;
59 RBAudioscrobbler *audioscrobbler;
60 GSettings *settings;
61
62 /* Used to request the user's profile data */
63 RBAudioscrobblerUser *user;
64 guint update_timeout_id;
65
66 /* List of radio stations owned by this page */
67 GList *radio_sources;
68
69 guint scrobbling_enabled_notification_id;
70
71 GtkWidget *main_vbox;
72 RBSourceToolbar *toolbar;
73
74 /* Login related UI */
75 GtkWidget *login_bar;
76 GtkWidget *login_status_label;
77 GtkWidget *login_response_button;
78
79 /* Profile UI */
80 GtkWidget *profile_window;
81
82 GtkWidget *user_info_area;
83 GtkWidget *profile_image;
84 GtkWidget *username_label;
85 GtkWidget *playcount_label;
86 GtkWidget *scrobbling_enabled_check;
87 GtkWidget *view_profile_link;
88
89 /* Scrobbler statistics */
90 GtkWidget *scrobbler_status_msg_label;
91 GtkWidget *scrobbler_queue_count_label;
92 GtkWidget *scrobbler_submit_count_label;
93 GtkWidget *scrobbler_submit_time_label;
94
95 /* Station creation UI */
96 GtkWidget *station_creator_type_combo;
97 GtkWidget *station_creator_arg_entry;
98
99 /* Profile data lists */
100 GtkWidget *recent_tracks_area;
101 GtkWidget *recent_tracks_wrap_box;
102 GtkWidget *top_tracks_area;
103 GtkWidget *top_tracks_wrap_box;
104 GtkWidget *loved_tracks_area;
105 GtkWidget *loved_tracks_wrap_box;
106 GtkWidget *top_artists_area;
107 GtkWidget *top_artists_wrap_box;
108 GtkWidget *recommended_artists_area;
109 GtkWidget *recommended_artists_wrap_box;
110
111 GHashTable *button_to_popup_menu_map;
112 GHashTable *popup_menu_to_data_map;
113
114 guint ui_merge_id;
115 GtkActionGroup *profile_action_group;
116 GtkActionGroup *service_action_group;
117 char *love_action_name;
118 char *ban_action_name;
119 char *download_action_name;
120 char *toolbar_path;
121 };
122
123
124 static void rb_audioscrobbler_profile_page_constructed (GObject *object);
125 static void rb_audioscrobbler_profile_page_dispose (GObject* object);
126 static void rb_audioscrobbler_profile_page_finalize (GObject *object);
127 static void rb_audioscrobbler_profile_page_get_property (GObject *object,
128 guint prop_id,
129 GValue *value,
130 GParamSpec *pspec);
131 static void rb_audioscrobbler_profile_page_set_property (GObject *object,
132 guint prop_id,
133 const GValue *value,
134 GParamSpec *pspec);
135
136 /* UI initialisation functions */
137 static void init_login_ui (RBAudioscrobblerProfilePage *page);
138 static void init_profile_ui (RBAudioscrobblerProfilePage *page);
139 static void init_actions (RBAudioscrobblerProfilePage *page);
140
141 /* login related callbacks */
142 static void login_bar_response_cb (GtkInfoBar *info_bar,
143 gint response_id,
144 RBAudioscrobblerProfilePage *page);
145 void logout_button_clicked_cb (GtkButton *button, RBAudioscrobblerProfilePage *page);
146 static void login_status_change_cb (RBAudioscrobblerAccount *account,
147 RBAudioscrobblerAccountLoginStatus status,
148 RBAudioscrobblerProfilePage *page);
149
150 /* scrobbling enabled preference */
151 void scrobbling_enabled_check_toggled_cb (GtkToggleButton *togglebutton,
152 RBAudioscrobblerProfilePage *page);
153 static void scrobbler_settings_changed_cb (GSettings *settings,
154 const char *key,
155 RBAudioscrobblerProfilePage *page);
156
157 /* callbacks from scrobbler object */
158 static void scrobbler_authentication_error_cb (RBAudioscrobbler *audioscrobbler,
159 RBAudioscrobblerProfilePage *page);
160 static void scrobbler_statistics_changed_cb (RBAudioscrobbler *audioscrobbler,
161 const char *status_msg,
162 guint queue_count,
163 guint submit_count,
164 const char *submit_time,
165 RBAudioscrobblerProfilePage *page);
166
167 static void playing_song_changed_cb (RBShellPlayer *player,
168 RhythmDBEntry *entry,
169 RBAudioscrobblerProfilePage *page);
170 static void update_service_actions_sensitivity (RBAudioscrobblerProfilePage *page, RhythmDBEntry *entry);
171
172 /* GtkAction callbacks */
173 static void love_track_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page);
174 static void ban_track_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page);
175 static void download_track_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page);
176 static void download_track_batch_complete_cb (RBTrackTransferBatch *batch, RBAudioscrobblerProfilePage *page);
177 static void refresh_profile_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page);
178
179 /* radio station creation/deletion */
180 void station_creator_button_clicked_cb (GtkButton *button, RBAudioscrobblerProfilePage *page);
181 static void load_radio_stations (RBAudioscrobblerProfilePage *page);
182 static void save_radio_stations (RBAudioscrobblerProfilePage *page);
183 static RBSource *add_radio_station (RBAudioscrobblerProfilePage *page,
184 const char *url,
185 const char *name);
186 static void radio_station_name_changed_cb (RBAudioscrobblerRadioSource *radio,
187 GParamSpec *spec,
188 RBAudioscrobblerProfilePage *page);
189
190 /* periodically attempts tp update the profile data */
191 static gboolean update_timeout_cb (RBAudioscrobblerProfilePage *page);
192
193 /* callbacks from user profile data requests */
194 static void user_info_updated_cb (RBAudioscrobblerUser *user,
195 RBAudioscrobblerUserData *info,
196 RBAudioscrobblerProfilePage *page);
197 static void recent_tracks_updated_cb (RBAudioscrobblerUser *user,
198 GPtrArray *recent_tracks,
199 RBAudioscrobblerProfilePage *page);
200 static void top_tracks_updated_cb (RBAudioscrobblerUser *user,
201 GPtrArray *top_tracks,
202 RBAudioscrobblerProfilePage *page);
203 static void loved_tracks_updated_cb (RBAudioscrobblerUser *user,
204 GPtrArray *loved_tracks,
205 RBAudioscrobblerProfilePage *page);
206 static void top_artists_updated_cb (RBAudioscrobblerUser *user,
207 GPtrArray *top_artists,
208 RBAudioscrobblerProfilePage *page);
209 static void recommended_artists_updated_cb (RBAudioscrobblerUser *user,
210 GPtrArray *recommended_artists,
211 RBAudioscrobblerProfilePage *page);
212
213 /* UI creation for profile data lists, eg top artists, loved tracks */
214 static void set_user_list (RBAudioscrobblerProfilePage *page,
215 GtkWidget *list_wrap_box,
216 GPtrArray *list_data);
217 static GtkWidget *create_list_button (RBAudioscrobblerProfilePage *page,
218 RBAudioscrobblerUserData *data,
219 int max_sibling_image_width);
220 static GtkWidget *create_popup_menu (RBAudioscrobblerProfilePage *page,
221 RBAudioscrobblerUserData *data);
222
223 /* callbacks from data list buttons and related popup menus */
224 static void list_item_clicked_cb (GtkButton *button, RBAudioscrobblerProfilePage *page);
225 static void list_item_view_url_activated_cb (GtkMenuItem *menuitem,
226 RBAudioscrobblerProfilePage *page);
227 static void list_item_listen_similar_artists_activated_cb (GtkMenuItem *menuitem,
228 RBAudioscrobblerProfilePage *page);
229 static void list_item_listen_top_fans_activated_cb (GtkMenuItem *menuitem,
230 RBAudioscrobblerProfilePage *page);
231
232 /* RBDisplayPage implementations */
233 static void impl_selected (RBDisplayPage *page);
234 static void impl_deselected (RBDisplayPage *page);
235 static gboolean impl_show_popup (RBDisplayPage *page);
236 static void impl_delete_thyself (RBDisplayPage *page);
237
238 enum {
239 PROP_0,
240 PROP_SERVICE,
241 PROP_TOOLBAR_PATH
242 };
243
244 static GtkActionEntry profile_actions [] =
245 {
246 { "AudioscrobblerProfileRefresh", NULL, N_("Refresh Profile"), NULL,
247 N_("Refresh your Profile"),
248 G_CALLBACK (refresh_profile_action_cb) }
249 };
250
251
252 G_DEFINE_DYNAMIC_TYPE (RBAudioscrobblerProfilePage, rb_audioscrobbler_profile_page, RB_TYPE_DISPLAY_PAGE)
253
254 RBDisplayPage *
255 rb_audioscrobbler_profile_page_new (RBShell *shell, GObject *plugin, RBAudioscrobblerService *service)
256 {
257 RBDisplayPage *page;
258 RhythmDB *db;
259 char *name;
260 gchar *icon_name;
261 gchar *icon_path;
262 gint icon_size;
263 GdkPixbuf *icon_pixbuf;
264
265 g_object_get (shell, "db", &db, NULL);
266 g_object_get (service, "name", &name, NULL);
267
268 icon_name = g_strconcat (rb_audioscrobbler_service_get_name (service), "-icon.png", NULL);
269 icon_path = rb_find_plugin_data_file (plugin, icon_name);
270 gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &icon_size, NULL);
271 icon_pixbuf = gdk_pixbuf_new_from_file_at_size (icon_path, icon_size, icon_size, NULL);
272
273 page = RB_DISPLAY_PAGE (g_object_new (RB_TYPE_AUDIOSCROBBLER_PROFILE_PAGE,
274 "shell", shell,
275 "plugin", plugin,
276 "name", name,
277 "pixbuf", icon_pixbuf,
278 "service", service,
279 NULL));
280
281 g_object_unref (db);
282 g_free (name);
283 g_free (icon_name);
284 g_free (icon_path);
285 g_object_unref (icon_pixbuf);
286
287 return page;
288 }
289
290 static void
291 rb_audioscrobbler_profile_page_class_init (RBAudioscrobblerProfilePageClass *klass)
292 {
293 GObjectClass *object_class;
294 RBDisplayPageClass *page_class;
295
296 object_class = G_OBJECT_CLASS (klass);
297 object_class->constructed = rb_audioscrobbler_profile_page_constructed;
298 object_class->dispose = rb_audioscrobbler_profile_page_dispose;
299 object_class->finalize = rb_audioscrobbler_profile_page_finalize;
300 object_class->get_property = rb_audioscrobbler_profile_page_get_property;
301 object_class->set_property = rb_audioscrobbler_profile_page_set_property;
302
303 page_class = RB_DISPLAY_PAGE_CLASS (klass);
304 page_class->selected = impl_selected;
305 page_class->deselected = impl_deselected;
306 page_class->show_popup = impl_show_popup;
307 page_class->delete_thyself = impl_delete_thyself;
308
309 g_object_class_install_property (object_class,
310 PROP_SERVICE,
311 g_param_spec_object ("service",
312 "Service",
313 "Audioscrobbler service for this page",
314 RB_TYPE_AUDIOSCROBBLER_SERVICE,
315 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
316 g_object_class_install_property (object_class,
317 PROP_TOOLBAR_PATH,
318 g_param_spec_string ("toolbar-path",
319 "toolbar path",
320 "toolbar UI path",
321 NULL,
322 G_PARAM_READABLE));
323
324 g_type_class_add_private (klass, sizeof (RBAudioscrobblerProfilePagePrivate));
325 }
326
327 static void
328 rb_audioscrobbler_profile_page_class_finalize (RBAudioscrobblerProfilePageClass *klass)
329 {
330 }
331
332 static void
333 rb_audioscrobbler_profile_page_init (RBAudioscrobblerProfilePage *page)
334 {
335 page->priv = G_TYPE_INSTANCE_GET_PRIVATE (page, RB_TYPE_AUDIOSCROBBLER_PROFILE_PAGE, RBAudioscrobblerProfilePagePrivate);
336
337 page->priv->button_to_popup_menu_map = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
338 page->priv->popup_menu_to_data_map = g_hash_table_new (g_direct_hash, g_direct_equal);
339 }
340
341 static void
342 rb_audioscrobbler_profile_page_constructed (GObject *object)
343 {
344 RBAudioscrobblerProfilePage *page;
345 RBShell *shell;
346 char *scrobbler_settings;
347 RBShellPlayer *shell_player;
348
349 RB_CHAIN_GOBJECT_METHOD (rb_audioscrobbler_profile_page_parent_class, constructed, object);
350
351 page = RB_AUDIOSCROBBLER_PROFILE_PAGE (object);
352 g_object_get (page, "shell", &shell, NULL);
353
354 rb_shell_append_display_page (shell, RB_DISPLAY_PAGE (page), RB_DISPLAY_PAGE_GROUP_LIBRARY);
355
356 g_object_get (shell, "shell-player", &shell_player, NULL);
357 g_signal_connect_object (shell_player,
358 "playing-song-changed",
359 G_CALLBACK (playing_song_changed_cb),
360 page, 0);
361 g_object_unref (shell_player);
362
363 /* create the UI */
364 page->priv->main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
365 gtk_box_pack_start (GTK_BOX (page), page->priv->main_vbox, TRUE, TRUE, 0);
366 gtk_widget_show (page->priv->main_vbox);
367
368 init_actions (page);
369
370 init_login_ui (page);
371 init_profile_ui (page);
372
373
374 /* create the user */
375 page->priv->user = rb_audioscrobbler_user_new (page->priv->service);
376 g_signal_connect (page->priv->user,
377 "user-info-updated",
378 G_CALLBACK (user_info_updated_cb),
379 page);
380 g_signal_connect (page->priv->user,
381 "recent-tracks-updated",
382 G_CALLBACK (recent_tracks_updated_cb),
383 page);
384 g_signal_connect (page->priv->user,
385 "top-tracks-updated",
386 G_CALLBACK (top_tracks_updated_cb),
387 page);
388 g_signal_connect (page->priv->user,
389 "loved-tracks-updated",
390 G_CALLBACK (loved_tracks_updated_cb),
391 page);
392 g_signal_connect (page->priv->user,
393 "top-artists-updated",
394 G_CALLBACK (top_artists_updated_cb),
395 page);
396 g_signal_connect (page->priv->user,
397 "recommended-artists-updated",
398 G_CALLBACK (recommended_artists_updated_cb),
399 page);
400
401 /* create the account */
402 page->priv->account = rb_audioscrobbler_account_new (page->priv->service);
403 g_signal_connect (page->priv->account,
404 "login-status-changed",
405 (GCallback)login_status_change_cb,
406 page);
407 /* scrobbler settings */
408 scrobbler_settings = g_strconcat (AUDIOSCROBBLER_SETTINGS_PATH "/",
409 rb_audioscrobbler_service_get_name (page->priv->service),
410 NULL);
411 page->priv->settings = g_settings_new_with_path (AUDIOSCROBBLER_SETTINGS_SCHEMA,
412 scrobbler_settings);
413 login_status_change_cb (page->priv->account,
414 rb_audioscrobbler_account_get_login_status (page->priv->account),
415 page);
416
417 g_signal_connect_object (page->priv->settings,
418 "changed",
419 G_CALLBACK (scrobbler_settings_changed_cb),
420 page, 0);
421 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page->priv->scrobbling_enabled_check),
422 g_settings_get_boolean (page->priv->settings,
423 AUDIOSCROBBLER_SCROBBLING_ENABLED_KEY));
424
425 g_object_unref (shell);
426 g_free (scrobbler_settings);
427 }
428
429 static void
430 rb_audioscrobbler_profile_page_dispose (GObject* object)
431 {
432 RBAudioscrobblerProfilePage *page;
433
434 page = RB_AUDIOSCROBBLER_PROFILE_PAGE (object);
435
436 if (page->priv->service != NULL) {
437 g_object_unref (page->priv->service);
438 page->priv->service = NULL;
439 }
440
441 if (page->priv->audioscrobbler != NULL) {
442 g_object_unref (page->priv->audioscrobbler);
443 page->priv->audioscrobbler = NULL;
444 }
445
446 if (page->priv->account != NULL) {
447 g_object_unref (page->priv->account);
448 page->priv->account = NULL;
449 }
450
451 if (page->priv->user != NULL) {
452 g_object_unref (page->priv->user);
453 page->priv->user = NULL;
454 }
455
456 if (page->priv->settings != NULL) {
457 g_object_unref (page->priv->settings);
458 page->priv->settings = NULL;
459 }
460
461 if (page->priv->button_to_popup_menu_map != NULL) {
462 g_hash_table_unref (page->priv->button_to_popup_menu_map);
463 page->priv->button_to_popup_menu_map = NULL;
464 }
465
466 if (page->priv->popup_menu_to_data_map != NULL) {
467 g_hash_table_unref (page->priv->popup_menu_to_data_map);
468 page->priv->popup_menu_to_data_map = NULL;
469 }
470
471 G_OBJECT_CLASS (rb_audioscrobbler_profile_page_parent_class)->dispose (object);
472 }
473
474 static void
475 rb_audioscrobbler_profile_page_finalize (GObject *object)
476 {
477 RBAudioscrobblerProfilePage *page;
478 page = RB_AUDIOSCROBBLER_PROFILE_PAGE (object);
479
480 g_free (page->priv->love_action_name);
481 g_free (page->priv->ban_action_name);
482 g_free (page->priv->download_action_name);
483 g_free (page->priv->toolbar_path);
484
485 G_OBJECT_CLASS (rb_audioscrobbler_profile_page_parent_class)->finalize (object);
486 }
487
488 static void
489 rb_audioscrobbler_profile_page_get_property (GObject *object,
490 guint prop_id,
491 GValue *value,
492 GParamSpec *pspec)
493 {
494 RBAudioscrobblerProfilePage *page = RB_AUDIOSCROBBLER_PROFILE_PAGE (object);
495 switch (prop_id) {
496 case PROP_TOOLBAR_PATH:
497 g_value_set_string (value, page->priv->toolbar_path);
498 break;
499 default:
500 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
501 break;
502 }
503 }
504
505 static void
506 rb_audioscrobbler_profile_page_set_property (GObject *object,
507 guint prop_id,
508 const GValue *value,
509 GParamSpec *pspec)
510 {
511 RBAudioscrobblerProfilePage *page = RB_AUDIOSCROBBLER_PROFILE_PAGE (object);
512 switch (prop_id) {
513 case PROP_SERVICE:
514 page->priv->service = g_value_dup_object (value);
515 break;
516 default:
517 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
518 break;
519 }
520 }
521
522 static void
523 init_login_ui (RBAudioscrobblerProfilePage *page)
524 {
525 GtkWidget *content_area;
526
527 page->priv->login_bar = gtk_info_bar_new ();
528 page->priv->login_status_label = gtk_label_new ("");
529 page->priv->login_response_button = gtk_button_new ();
530 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (page->priv->login_bar));
531 gtk_container_add (GTK_CONTAINER (content_area), page->priv->login_status_label);
532 page->priv->login_response_button =
533 gtk_info_bar_add_button (GTK_INFO_BAR (page->priv->login_bar),
534 "", GTK_RESPONSE_OK);
535 g_signal_connect (page->priv->login_bar,
536 "response",
537 G_CALLBACK (login_bar_response_cb),
538 page);
539 gtk_box_pack_start (GTK_BOX (page->priv->main_vbox), page->priv->login_bar, FALSE, FALSE, 0);
540 }
541
542 static void
543 init_profile_ui (RBAudioscrobblerProfilePage *page)
544 {
545 GObject *plugin;
546 char *builder_file;
547 GtkBuilder *builder;
548 GtkWidget *combo_container;
549 int i;
550
551 g_object_get (page, "plugin", &plugin, NULL);
552
553 builder_file = rb_find_plugin_data_file (plugin, "audioscrobbler-profile.ui");
554 g_assert (builder_file != NULL);
555 builder = rb_builder_load (builder_file, page);
556
557 page->priv->profile_window = GTK_WIDGET (gtk_builder_get_object (builder, "profile_window"));
558
559 page->priv->user_info_area = GTK_WIDGET (gtk_builder_get_object (builder, "user_info_area"));
560 page->priv->profile_image = GTK_WIDGET (gtk_builder_get_object (builder, "profile_image"));
561 page->priv->username_label = GTK_WIDGET (gtk_builder_get_object (builder, "username_label"));
562 page->priv->playcount_label = GTK_WIDGET (gtk_builder_get_object (builder, "playcount_label"));
563 page->priv->scrobbling_enabled_check = GTK_WIDGET (gtk_builder_get_object (builder, "scrobbling_enabled_check"));
564 page->priv->view_profile_link = GTK_WIDGET (gtk_builder_get_object (builder, "view_profile_link"));
565
566 /* scrobbler statistics */
567 page->priv->scrobbler_status_msg_label = GTK_WIDGET (gtk_builder_get_object (builder, "scrobbler_status_msg_label"));
568 page->priv->scrobbler_queue_count_label = GTK_WIDGET (gtk_builder_get_object (builder, "scrobbler_queue_count_label"));
569 page->priv->scrobbler_submit_count_label = GTK_WIDGET (gtk_builder_get_object (builder, "scrobbler_submit_count_label"));
570 page->priv->scrobbler_submit_time_label = GTK_WIDGET (gtk_builder_get_object (builder, "scrobbler_submit_time_label"));
571
572 /* station creator */
573 page->priv->station_creator_arg_entry = GTK_WIDGET (gtk_builder_get_object (builder, "station_creator_arg_entry"));
574 combo_container = GTK_WIDGET (gtk_builder_get_object (builder, "station_creator_combo_container"));
575 page->priv->station_creator_type_combo = gtk_combo_box_text_new ();
576 gtk_container_add (GTK_CONTAINER (combo_container), page->priv->station_creator_type_combo);
577 for (i = 0; i < RB_AUDIOSCROBBLER_RADIO_TYPE_LAST; i++) {
578 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (page->priv->station_creator_type_combo),
579 rb_audioscrobbler_radio_type_get_text (i));
580 }
581 gtk_combo_box_set_active (GTK_COMBO_BOX (page->priv->station_creator_type_combo), 0);
582 gtk_widget_show (page->priv->station_creator_type_combo);
583
584 /* lists of data */
585 page->priv->recent_tracks_area = GTK_WIDGET (gtk_builder_get_object (builder, "recent_tracks_area"));
586 page->priv->recent_tracks_wrap_box = egg_wrap_box_new (EGG_WRAP_ALLOCATE_HOMOGENEOUS,
587 EGG_WRAP_BOX_SPREAD_EXPAND,
588 EGG_WRAP_BOX_SPREAD_START,
589 2, 2);
590 gtk_box_pack_end (GTK_BOX (page->priv->recent_tracks_area),
591 page->priv->recent_tracks_wrap_box,
592 TRUE, TRUE, 0);
593
594 page->priv->top_tracks_area = GTK_WIDGET (gtk_builder_get_object (builder, "top_tracks_area"));
595 page->priv->top_tracks_wrap_box = egg_wrap_box_new (EGG_WRAP_ALLOCATE_HOMOGENEOUS,
596 EGG_WRAP_BOX_SPREAD_EXPAND,
597 EGG_WRAP_BOX_SPREAD_START,
598 2, 2);
599 gtk_box_pack_end (GTK_BOX (page->priv->top_tracks_area),
600 page->priv->top_tracks_wrap_box,
601 TRUE, TRUE, 0);
602
603 page->priv->loved_tracks_area = GTK_WIDGET (gtk_builder_get_object (builder, "loved_tracks_area"));
604 page->priv->loved_tracks_wrap_box = egg_wrap_box_new (EGG_WRAP_ALLOCATE_HOMOGENEOUS,
605 EGG_WRAP_BOX_SPREAD_EXPAND,
606 EGG_WRAP_BOX_SPREAD_START,
607 2, 2);
608 gtk_box_pack_end (GTK_BOX (page->priv->loved_tracks_area),
609 page->priv->loved_tracks_wrap_box,
610 TRUE, TRUE, 0);
611
612 page->priv->top_artists_area = GTK_WIDGET (gtk_builder_get_object (builder, "top_artists_area"));
613 page->priv->top_artists_wrap_box = egg_wrap_box_new (EGG_WRAP_ALLOCATE_HOMOGENEOUS,
614 EGG_WRAP_BOX_SPREAD_EXPAND,
615 EGG_WRAP_BOX_SPREAD_START,
616 2, 2);
617 gtk_box_pack_end (GTK_BOX (page->priv->top_artists_area),
618 page->priv->top_artists_wrap_box,
619 TRUE, TRUE, 0);
620
621 page->priv->recommended_artists_area = GTK_WIDGET (gtk_builder_get_object (builder, "recommended_artists_area"));
622 page->priv->recommended_artists_wrap_box = egg_wrap_box_new (EGG_WRAP_ALLOCATE_HOMOGENEOUS,
623 EGG_WRAP_BOX_SPREAD_EXPAND,
624 EGG_WRAP_BOX_SPREAD_START,
625 2, 2);
626 gtk_box_pack_end (GTK_BOX (page->priv->recommended_artists_area),
627 page->priv->recommended_artists_wrap_box,
628 TRUE, TRUE, 0);
629
630 /* pack profile into main vbox */
631 gtk_box_pack_start (GTK_BOX (page->priv->main_vbox), page->priv->profile_window, TRUE, TRUE, 0);
632
633
634 g_object_unref (plugin);
635 g_free (builder_file);
636 g_object_unref (builder);
637 }
638
639 static void
640 init_actions (RBAudioscrobblerProfilePage *page)
641 {
642 char *ui_file;
643 RBShell *shell;
644 RBShellPlayer *player;
645 GObject *plugin;
646 GtkUIManager *ui_manager;
647 RhythmDBEntry *entry;
648 char *group_name;
649 char *toolbar_name;
650
651 g_object_get (page, "shell", &shell, "plugin", &plugin, "ui-manager", &ui_manager, NULL);
652 ui_file = rb_find_plugin_data_file (plugin, "audioscrobbler-profile-ui.xml");
653 page->priv->ui_merge_id = gtk_ui_manager_add_ui_from_file (ui_manager, ui_file, NULL);
654
655 page->priv->profile_action_group = _rb_display_page_register_action_group (RB_DISPLAY_PAGE (page),
656 "AudioscrobblerProfileActions",
657 NULL, 0,
658 page);
659 _rb_action_group_add_display_page_actions (page->priv->profile_action_group,
660 G_OBJECT (shell),
661 profile_actions,
662 G_N_ELEMENTS (profile_actions));
663
664 /* Unfortunately we can't use the usual trick of declaring a static array of GtkActionEntry,
665 * and simply using _rb_source_register_action_group with that array.
666 * This is because each instance of this page needs its own love and ban actions
667 * so tracks can be loved/banned differently for different audioscrobbler services.
668 */
669 group_name = g_strdup_printf ("%sActions", rb_audioscrobbler_service_get_name (page->priv->service));
670 page->priv->love_action_name = g_strdup_printf ("%sLoveTrack", rb_audioscrobbler_service_get_name (page->priv->service));
671 page->priv->ban_action_name = g_strdup_printf ("%sBanTrack", rb_audioscrobbler_service_get_name (page->priv->service));
672 page->priv->download_action_name = g_strdup_printf ("%sDownloadTrack", rb_audioscrobbler_service_get_name (page->priv->service));
673
674 GtkActionEntry service_actions [] =
675 {
676 { page->priv->love_action_name, "emblem-favorite", N_("Love"), NULL,
677 N_("Mark this song as loved"),
678 G_CALLBACK (love_track_action_cb) },
679 { page->priv->ban_action_name, GTK_STOCK_CANCEL, N_("Ban"), NULL,
680 N_("Ban the current track from being played again"),
681 G_CALLBACK (ban_track_action_cb) },
682 { page->priv->download_action_name, GTK_STOCK_SAVE, N_("Download"), NULL,
683 N_("Download the currently playing track"),
684 G_CALLBACK (download_track_action_cb) }
685 };
686
687 page->priv->service_action_group = _rb_display_page_register_action_group (RB_DISPLAY_PAGE (page),
688 group_name,
689 service_actions,
690 G_N_ELEMENTS (service_actions),
691 page);
692 g_object_get (shell, "shell-player", &player, NULL);
693 entry = rb_shell_player_get_playing_entry (player);
694 update_service_actions_sensitivity (page, entry);
695 if (entry != NULL) {
696 rhythmdb_entry_unref (entry);
697 }
698 g_object_unref (player);
699
700 /* set up toolbar */
701 toolbar_name = g_strdup_printf ("%sSourceToolBar", rb_audioscrobbler_service_get_name (page->priv->service));
702 page->priv->toolbar_path = g_strdup_printf ("/%sSourceToolBar", rb_audioscrobbler_service_get_name (page->priv->service));
703 gtk_ui_manager_add_ui (ui_manager, page->priv->ui_merge_id, "/", toolbar_name, NULL, GTK_UI_MANAGER_TOOLBAR, TRUE);
704 gtk_ui_manager_add_ui (ui_manager, page->priv->ui_merge_id, page->priv->toolbar_path, "Love", page->priv->love_action_name, GTK_UI_MANAGER_TOOLITEM, FALSE);
705 gtk_ui_manager_add_ui (ui_manager, page->priv->ui_merge_id, page->priv->toolbar_path, "Ban", page->priv->ban_action_name, GTK_UI_MANAGER_TOOLITEM, FALSE);
706 gtk_ui_manager_add_ui (ui_manager, page->priv->ui_merge_id, page->priv->toolbar_path, "Download", page->priv->download_action_name, GTK_UI_MANAGER_TOOLITEM, FALSE);
707
708 page->priv->toolbar = rb_source_toolbar_new (RB_DISPLAY_PAGE (page), ui_manager);
709 gtk_box_pack_start (GTK_BOX (page->priv->main_vbox), GTK_WIDGET (page->priv->toolbar), FALSE, FALSE, 0);
710 gtk_widget_show (GTK_WIDGET (page->priv->toolbar));
711
712 g_free (ui_file);
713 g_free (toolbar_name);
714 g_object_unref (shell);
715 g_object_unref (plugin);
716 g_object_unref (ui_manager);
717 g_free (group_name);
718 }
719
720 static void
721 login_bar_response_cb (GtkInfoBar *info_bar,
722 gint response_id,
723 RBAudioscrobblerProfilePage *page)
724 {
725 switch (rb_audioscrobbler_account_get_login_status (page->priv->account)) {
726 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_OUT:
727 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_AUTH_ERROR:
728 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_CONNECTION_ERROR:
729 rb_audioscrobbler_account_authenticate (page->priv->account);
730 break;
731 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGING_IN:
732 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_IN:
733 rb_audioscrobbler_account_logout (page->priv->account);
734 break;
735 default:
736 g_assert_not_reached ();
737 break;
738 }
739 }
740
741 void
742 logout_button_clicked_cb (GtkButton *button,
743 RBAudioscrobblerProfilePage *page)
744 {
745 rb_audioscrobbler_account_logout (page->priv->account);
746 }
747
748 static void
749 login_status_change_cb (RBAudioscrobblerAccount *account,
750 RBAudioscrobblerAccountLoginStatus status,
751 RBAudioscrobblerProfilePage *page)
752 {
753 const char *username;
754 const char *session_key;
755 char *label_text = NULL;
756 char *button_text = NULL;
757 gboolean show_login_bar;
758 gboolean show_profile;
759
760 username = rb_audioscrobbler_account_get_username (page->priv->account);
761 session_key = rb_audioscrobbler_account_get_session_key (page->priv->account);
762
763 /* delete old scrobbler */
764 if (page->priv->audioscrobbler != NULL) {
765 g_object_unref (page->priv->audioscrobbler);
766 page->priv->audioscrobbler = NULL;
767 }
768
769 /* create new scrobbler if new user has logged in and scrobbling is enabled */
770 if (status == RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_IN &&
771 g_settings_get_boolean (page->priv->settings, AUDIOSCROBBLER_SCROBBLING_ENABLED_KEY)) {
772 RBShell *shell;
773 RBShellPlayer *shell_player;
774 g_object_get (page, "shell", &shell, NULL);
775 g_object_get (shell, "shell-player", &shell_player, NULL);
776 page->priv->audioscrobbler =
777 rb_audioscrobbler_new (page->priv->service,
778 shell_player,
779 rb_audioscrobbler_account_get_username (page->priv->account),
780 rb_audioscrobbler_account_get_session_key (page->priv->account));
781 g_signal_connect (page->priv->audioscrobbler,
782 "authentication-error",
783 G_CALLBACK (scrobbler_authentication_error_cb),
784 page);
785 g_signal_connect (page->priv->audioscrobbler,
786 "statistics-changed",
787 G_CALLBACK (scrobbler_statistics_changed_cb),
788 page);
789 rb_audioscrobbler_statistics_changed (page->priv->audioscrobbler);
790 g_object_unref (shell_player);
791 g_object_unref (shell);
792 }
793
794 /* set the new user details */
795 rb_audioscrobbler_user_set_authentication_details (page->priv->user, username, session_key);
796 if (username != NULL) {
797 rb_audioscrobbler_user_update (page->priv->user);
798 }
799
800 load_radio_stations (page);
801
802 /* update the login ui */
803 switch (status) {
804 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_OUT:
805 show_login_bar = TRUE;
806 show_profile = FALSE;
807 label_text = g_strdup (_("You are not currently logged in."));
808 button_text = g_strdup (_("Log in"));
809 gtk_info_bar_set_message_type (GTK_INFO_BAR (page->priv->login_bar), GTK_MESSAGE_INFO);
810 break;
811 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGING_IN:
812 show_login_bar = TRUE;
813 show_profile = FALSE;
814 label_text = g_strdup (_("Waiting for authentication..."));
815 button_text = g_strdup (_("Cancel"));
816 gtk_info_bar_set_message_type (GTK_INFO_BAR (page->priv->login_bar), GTK_MESSAGE_INFO);
817 break;
818 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_IN:
819 show_login_bar = FALSE;
820 show_profile = TRUE;
821 break;
822 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_AUTH_ERROR:
823 show_login_bar = TRUE;
824 show_profile = FALSE;
825 label_text = g_strdup (_("Authentication error. Please try logging in again."));
826 button_text = g_strdup (_("Log in"));
827 gtk_info_bar_set_message_type (GTK_INFO_BAR (page->priv->login_bar), GTK_MESSAGE_WARNING);
828 break;
829 case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_CONNECTION_ERROR:
830 show_login_bar = TRUE;
831 show_profile = FALSE;
832 label_text = g_strdup (_("Connection error. Please try logging in again."));
833 button_text = g_strdup (_("Log in"));
834 gtk_info_bar_set_message_type (GTK_INFO_BAR (page->priv->login_bar), GTK_MESSAGE_WARNING);
835 break;
836 default:
837 g_assert_not_reached ();
838 break;
839 }
840
841 gtk_label_set_label (GTK_LABEL (page->priv->login_status_label), label_text);
842 gtk_button_set_label (GTK_BUTTON (page->priv->login_response_button), button_text);
843 if (show_login_bar == TRUE) {
844 gtk_widget_show_all (page->priv->login_bar);
845 } else {
846 gtk_widget_hide (page->priv->login_bar);
847 }
848 if (show_profile == TRUE) {
849 gtk_label_set_label (GTK_LABEL (page->priv->username_label),
850 username);
851 gtk_widget_show (page->priv->username_label);
852 gtk_widget_show (page->priv->profile_window);
853 } else {
854 gtk_widget_hide (page->priv->profile_window);
855 }
856
857 g_free (label_text);
858 g_free (button_text);
859 }
860
861 void
862 scrobbling_enabled_check_toggled_cb (GtkToggleButton *togglebutton,
863 RBAudioscrobblerProfilePage *page)
864 {
865 g_settings_set_boolean (page->priv->settings,
866 AUDIOSCROBBLER_SCROBBLING_ENABLED_KEY,
867 gtk_toggle_button_get_active (togglebutton));
868 }
869
870 static void
871 scrobbler_settings_changed_cb (GSettings *settings,
872 const char *key,
873 RBAudioscrobblerProfilePage *page)
874 {
875 gboolean enabled;
876 if (g_strcmp0 (key, AUDIOSCROBBLER_SCROBBLING_ENABLED_KEY) != 0) {
877 return;
878 }
879
880 enabled = g_settings_get_boolean (settings, key);
881 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page->priv->scrobbling_enabled_check),
882 enabled);
883
884 if (page->priv->audioscrobbler != NULL && enabled == FALSE) {
885 g_object_unref (page->priv->audioscrobbler);
886 page->priv->audioscrobbler = NULL;
887 gtk_label_set_label (GTK_LABEL (page->priv->scrobbler_status_msg_label),
888 _("Disabled"));
889 } else if (page->priv->audioscrobbler == NULL && enabled == TRUE) {
890 RBShell *shell;
891 RBShellPlayer *shell_player;
892 g_object_get (page, "shell", &shell, NULL);
893 g_object_get (shell, "shell-player", &shell_player, NULL);
894 page->priv->audioscrobbler =
895 rb_audioscrobbler_new (page->priv->service,
896 shell_player,
897 rb_audioscrobbler_account_get_username (page->priv->account),
898 rb_audioscrobbler_account_get_session_key (page->priv->account));
899 g_signal_connect (page->priv->audioscrobbler,
900 "authentication-error",
901 G_CALLBACK (scrobbler_authentication_error_cb),
902 page);
903 g_signal_connect (page->priv->audioscrobbler,
904 "statistics-changed",
905 G_CALLBACK (scrobbler_statistics_changed_cb),
906 page);
907 rb_audioscrobbler_statistics_changed (page->priv->audioscrobbler);
908 g_object_unref (shell_player);
909 g_object_unref (shell);
910 }
911 }
912
913 static void
914 scrobbler_authentication_error_cb (RBAudioscrobbler *audioscrobbler,
915 RBAudioscrobblerProfilePage *page)
916 {
917 rb_audioscrobbler_account_notify_of_auth_error (page->priv->account);
918 }
919
920 static void
921 scrobbler_statistics_changed_cb (RBAudioscrobbler *audioscrobbler,
922 const char *status_msg,
923 guint queue_count,
924 guint submit_count,
925 const char *submit_time,
926 RBAudioscrobblerProfilePage *page)
927 {
928 char *queue_count_text;
929 char *submit_count_text;
930
931 gtk_label_set_text (GTK_LABEL (page->priv->scrobbler_status_msg_label), status_msg);
932
933 queue_count_text = g_strdup_printf ("%u", queue_count);
934 gtk_label_set_text (GTK_LABEL (page->priv->scrobbler_queue_count_label), queue_count_text);
935
936 submit_count_text = g_strdup_printf ("%u", submit_count);
937 gtk_label_set_text (GTK_LABEL (page->priv->scrobbler_submit_count_label), submit_count_text);
938
939 gtk_label_set_text (GTK_LABEL (page->priv->scrobbler_submit_time_label), submit_time);
940
941 g_free (queue_count_text);
942 g_free (submit_count_text);
943 }
944
945 static void
946 playing_song_changed_cb (RBShellPlayer *player,
947 RhythmDBEntry *entry,
948 RBAudioscrobblerProfilePage *page)
949 {
950 update_service_actions_sensitivity (page, entry);
951 }
952
953 static void
954 update_service_actions_sensitivity (RBAudioscrobblerProfilePage *page, RhythmDBEntry *entry)
955 {
956 GtkAction *love;
957 GtkAction *ban;
958 GtkAction *download;
959
960 /* enable love/ban if an entry is playing */
961 love = gtk_action_group_get_action (page->priv->service_action_group, page->priv->love_action_name);
962 ban = gtk_action_group_get_action (page->priv->service_action_group, page->priv->ban_action_name);
963 if (entry == NULL) {
964 gtk_action_set_sensitive (love, FALSE);
965 gtk_action_set_sensitive (ban, FALSE);
966 } else {
967 gtk_action_set_sensitive (love, TRUE);
968 gtk_action_set_sensitive (ban, TRUE);
969 }
970
971 /* enable download if the playing entry is a radio track from this service which provides a download url */
972 download = gtk_action_group_get_action (page->priv->service_action_group, page->priv->download_action_name);
973 if (entry != NULL &&
974 rhythmdb_entry_get_entry_type (entry) == RHYTHMDB_ENTRY_TYPE_AUDIOSCROBBLER_RADIO_TRACK) {
975 RBAudioscrobblerRadioTrackData *data;
976 data = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RBAudioscrobblerRadioTrackData);
977
978 if (data->service == page->priv->service && data->download_url != NULL) {
979 gtk_action_set_sensitive (download, TRUE);
980 } else {
981 gtk_action_set_sensitive (download, FALSE);
982 }
983 } else {
984 gtk_action_set_sensitive (download, FALSE);
985 }
986 }
987
988 static void
989 love_track_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page)
990 {
991 RBShell *shell;
992 RBShellPlayer *shell_player;
993 RhythmDBEntry *playing;
994 GtkAction *ban_action;
995
996 g_object_get (page, "shell", &shell, NULL);
997 g_object_get (shell, "shell-player", &shell_player, NULL);
998 playing = rb_shell_player_get_playing_entry (shell_player);
999
1000 if (playing != NULL) {
1001 rb_audioscrobbler_user_love_track (page->priv->user,
1002 rhythmdb_entry_get_string (playing, RHYTHMDB_PROP_TITLE),
1003 rhythmdb_entry_get_string (playing, RHYTHMDB_PROP_ARTIST));
1004 rhythmdb_entry_unref (playing);
1005 }
1006
1007 /* disable love/ban */
1008 gtk_action_set_sensitive (action, FALSE);
1009 ban_action = gtk_action_group_get_action (page->priv->service_action_group, page->priv->ban_action_name);
1010 gtk_action_set_sensitive (ban_action, FALSE);
1011
1012 g_object_unref (shell_player);
1013 g_object_unref (shell);
1014 }
1015
1016 static void
1017 ban_track_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page)
1018 {
1019 RBShell *shell;
1020 RBShellPlayer *shell_player;
1021 RhythmDBEntry *playing;
1022
1023 g_object_get (page, "shell", &shell, NULL);
1024 g_object_get (shell, "shell-player", &shell_player, NULL);
1025 playing = rb_shell_player_get_playing_entry (shell_player);
1026
1027 if (playing != NULL) {
1028 rb_audioscrobbler_user_ban_track (page->priv->user,
1029 rhythmdb_entry_get_string (playing, RHYTHMDB_PROP_TITLE),
1030 rhythmdb_entry_get_string (playing, RHYTHMDB_PROP_ARTIST));
1031 rhythmdb_entry_unref (playing);
1032 }
1033
1034 /* skip to next track */
1035 rb_shell_player_do_next (shell_player, NULL);
1036
1037 g_object_unref (shell_player);
1038 g_object_unref (shell);
1039 }
1040
1041 static void
1042 download_track_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page)
1043 {
1044 RBShell *shell;
1045 RBShellPlayer *shell_player;
1046 RhythmDBEntry *playing;
1047
1048 /* disable the action */
1049 gtk_action_set_sensitive (action, FALSE);
1050
1051 g_object_get (page, "shell", &shell, NULL);
1052 g_object_get (shell, "shell-player", &shell_player, NULL);
1053 playing = rb_shell_player_get_playing_entry (shell_player);
1054
1055 if (playing != NULL &&
1056 rhythmdb_entry_get_entry_type (playing) == RHYTHMDB_ENTRY_TYPE_AUDIOSCROBBLER_RADIO_TRACK) {
1057 RBAudioscrobblerRadioTrackData *data;
1058 data = RHYTHMDB_ENTRY_GET_TYPE_DATA (playing, RBAudioscrobblerRadioTrackData);
1059
1060 if (data->download_url != NULL) {
1061 RhythmDB *db;
1062 RBSource *library;
1063 RhythmDBEntry *download;
1064 GValue val = { 0, };
1065 RBTrackTransferBatch *batch;
1066
1067 /* we need the library source to paste into */
1068 g_object_get (shell, "db", &db, "library-source", &library, NULL);
1069
1070 /* create a new entry to paste */
1071 download = rhythmdb_entry_new (db,
1072 RHYTHMDB_ENTRY_TYPE_AUDIOSCROBBLER_RADIO_TRACK, /* not really, but it needs a type */
1073 data->download_url);
1074
1075 g_value_init (&val, G_TYPE_STRING);
1076 g_value_set_string (&val, rhythmdb_entry_get_string (playing, RHYTHMDB_PROP_TITLE));
1077 rhythmdb_entry_set (db, download, RHYTHMDB_PROP_TITLE, &val);
1078 g_value_unset (&val);
1079 g_value_init (&val, G_TYPE_STRING);
1080 g_value_set_string (&val, rhythmdb_entry_get_string (playing, RHYTHMDB_PROP_ARTIST));
1081 rhythmdb_entry_set (db, download, RHYTHMDB_PROP_ARTIST, &val);
1082 g_value_unset (&val);
1083 g_value_init (&val, G_TYPE_STRING);
1084 g_value_set_string (&val, rhythmdb_entry_get_string (playing, RHYTHMDB_PROP_ALBUM));
1085 rhythmdb_entry_set (db, download, RHYTHMDB_PROP_ALBUM, &val);
1086 g_value_unset (&val);
1087
1088 rb_debug ("downloading track from %s", data->download_url);
1089 batch = rb_source_paste (library, g_list_append (NULL, download));
1090
1091 if (batch == NULL) {
1092 /* delete the entry we just created */
1093 rhythmdb_entry_delete (db, download);
1094 rhythmdb_entry_unref (download);
1095 } else {
1096 /* connect a callback to delete the entry when transfer is done */
1097 g_signal_connect_object (batch,
1098 "complete",
1099 G_CALLBACK (download_track_batch_complete_cb),
1100 page,
1101 0);
1102 }
1103
1104 g_object_unref (db);
1105 g_object_unref (library);
1106 } else {
1107 rb_debug ("cannot download: no download url");
1108 }
1109 rhythmdb_entry_unref (playing);
1110 } else {
1111 rb_debug ("cannot download: playing entry is not an audioscrobbler radio track");
1112 }
1113
1114 g_object_unref (shell_player);
1115 g_object_unref (shell);
1116 }
1117
1118 static void
1119 download_track_batch_complete_cb (RBTrackTransferBatch *batch,
1120 RBAudioscrobblerProfilePage *page)
1121 {
1122 GList *entries;
1123 RBShell *shell;
1124 RhythmDB *db;
1125 GList *i;
1126
1127 g_object_get (batch, "entry-list", &entries, NULL);
1128 g_object_get (page, "shell", &shell, NULL);
1129 g_object_get (shell, "db", &db, NULL);
1130
1131 /* delete the entries which were transfered.
1132 * need to call unref twice as g_object_get will have reffed them
1133 */
1134 for (i = entries; i != NULL; i = i->next) {
1135 rhythmdb_entry_delete (db, i->data);
1136 rhythmdb_entry_unref (i->data);
1137 rhythmdb_entry_unref (i->data);
1138 }
1139
1140 g_list_free (entries);
1141 g_object_unref (shell);
1142 g_object_unref (db);
1143 }
1144
1145 static void
1146 refresh_profile_action_cb (GtkAction *action, RBAudioscrobblerProfilePage *page)
1147 {
1148 rb_audioscrobbler_user_force_update (page->priv->user);
1149 }
1150
1151 void
1152 station_creator_button_clicked_cb (GtkButton *button,
1153 RBAudioscrobblerProfilePage *page)
1154 {
1155 const char *arg;
1156
1157 arg = gtk_entry_get_text (GTK_ENTRY (page->priv->station_creator_arg_entry));
1158
1159 if (arg[0] != '\0') {
1160 RBAudioscrobblerRadioType type;
1161 char *url;
1162 char *name;
1163 RBSource *radio;
1164 RBShell *shell;
1165 RBDisplayPageTree *page_tree;
1166
1167 type = gtk_combo_box_get_active (GTK_COMBO_BOX (page->priv->station_creator_type_combo));
1168
1169 url = g_strdup_printf (rb_audioscrobbler_radio_type_get_url (type),
1170 arg);
1171 name = g_strdup_printf (rb_audioscrobbler_radio_type_get_default_name (type),
1172 arg);
1173
1174 radio = add_radio_station (page, url, name);
1175 g_object_get (page, "shell", &shell, NULL);
1176 g_object_get (shell, "display-page-tree", &page_tree, NULL);
1177 rb_display_page_tree_select (page_tree, RB_DISPLAY_PAGE (radio));
1178
1179 gtk_entry_set_text (GTK_ENTRY (page->priv->station_creator_arg_entry), "");
1180
1181 g_free (url);
1182 g_free (name);
1183 g_object_unref (shell);
1184 g_object_unref (page_tree);
1185 }
1186 }
1187
1188 /* delete old user's radio sources and load ones for new user */
1189 static void
1190 load_radio_stations (RBAudioscrobblerProfilePage *page)
1191 {
1192 /* destroy existing sources */
1193 while (page->priv->radio_sources != NULL) {
1194 rb_display_page_delete_thyself (RB_DISPLAY_PAGE (page->priv->radio_sources->data));
1195 page->priv->radio_sources = g_list_remove (page->priv->radio_sources, page->priv->radio_sources->data);
1196 }
1197
1198 /* load the user's saved stations */
1199 if (rb_audioscrobbler_account_get_username (page->priv->account) != NULL) {
1200 JsonParser *parser;
1201 char *filename;
1202
1203 parser = json_parser_new ();
1204 filename = g_build_filename (rb_user_data_dir (),
1205 "audioscrobbler",
1206 "stations",
1207 rb_audioscrobbler_service_get_name (page->priv->service),
1208 rb_audioscrobbler_account_get_username (page->priv->account),
1209 NULL);
1210
1211 if (json_parser_load_from_file (parser, filename, NULL)) {
1212 JsonArray *stations;
1213 int i;
1214
1215 stations = json_node_get_array (json_parser_get_root (parser));
1216
1217 for (i = 0; i < json_array_get_length (stations); i++) {
1218 JsonObject *station;
1219 const char *name;
1220 const char *url;
1221 RBSource *radio;
1222
1223 station = json_array_get_object_element (stations, i);
1224 name = json_object_get_string_member (station, "name");
1225 url = json_object_get_string_member (station, "url");
1226
1227 radio = rb_audioscrobbler_radio_source_new (page,
1228 page->priv->service,
1229 rb_audioscrobbler_account_get_username (page->priv->account),
1230 rb_audioscrobbler_account_get_session_key (page->priv->account),
1231 name,
1232 url);
1233 page->priv->radio_sources = g_list_append (page->priv->radio_sources, radio);
1234 g_signal_connect (radio, "notify::name",
1235 G_CALLBACK (radio_station_name_changed_cb),
1236 page);
1237 }
1238 }
1239
1240 /* if the list of stations is still empty then add some defaults */
1241 if (page->priv->radio_sources == NULL) {
1242 char *url;
1243 char *name;
1244
1245 /* user's library */
1246 url = g_strdup_printf (rb_audioscrobbler_radio_type_get_url (RB_AUDIOSCROBBLER_RADIO_TYPE_LIBRARY),
1247 rb_audioscrobbler_account_get_username (page->priv->account));
1248 name = g_strdup (_("My Library"));
1249 add_radio_station (page, url, name);
1250 g_free (url);
1251 g_free (name);
1252
1253 /* user's recommendations */
1254 url = g_strdup_printf (rb_audioscrobbler_radio_type_get_url (RB_AUDIOSCROBBLER_RADIO_TYPE_RECOMMENDATION),
1255 rb_audioscrobbler_account_get_username (page->priv->account));
1256 name = g_strdup (_("My Recommendations"));
1257 add_radio_station (page, url, name);
1258 g_free (url);
1259 g_free (name);
1260
1261 /* user's neighbourhood */
1262 url = g_strdup_printf (rb_audioscrobbler_radio_type_get_url (RB_AUDIOSCROBBLER_RADIO_TYPE_NEIGHBOURS),
1263 rb_audioscrobbler_account_get_username (page->priv->account));
1264 name = g_strdup (_("My Neighbourhood"));
1265 add_radio_station (page, url, name);
1266 g_free (url);
1267 g_free (name);
1268
1269 /* rhythmbox group */
1270 url = g_strdup_printf (rb_audioscrobbler_radio_type_get_url (RB_AUDIOSCROBBLER_RADIO_TYPE_GROUP),
1271 "rhythmbox");
1272 name = g_strdup_printf (rb_audioscrobbler_radio_type_get_default_name (RB_AUDIOSCROBBLER_RADIO_TYPE_GROUP),
1273 "Rhythmbox");
1274 add_radio_station (page, url, name);
1275 g_free (url);
1276 g_free (name);
1277 }
1278
1279 g_object_unref (parser);
1280 g_free (filename);
1281 }
1282 }
1283
1284 /* save user's radio stations */
1285 static void
1286 save_radio_stations (RBAudioscrobblerProfilePage *page)
1287 {
1288 JsonNode *root;
1289 JsonArray *stations;
1290 GList *i;
1291 JsonGenerator *generator;
1292 char *filename;
1293 char *uri;
1294 GError *error;
1295
1296 root = json_node_new (JSON_NODE_ARRAY);
1297 stations = json_array_new ();
1298
1299 for (i = page->priv->radio_sources; i != NULL; i = i->next) {
1300 JsonObject *station;
1301 char *name;
1302 char *url;
1303
1304 g_object_get (i->data, "name", &name, "station-url", &url, NULL);
1305 station = json_object_new ();
1306 json_object_set_string_member (station, "name", name);
1307 json_object_set_string_member (station, "url", url);
1308 json_array_add_object_element (stations, station);
1309
1310 g_free (name);
1311 g_free (url);
1312 }
1313
1314 json_node_take_array (root, stations);
1315
1316 generator = json_generator_new ();
1317 json_generator_set_root (generator, root);
1318
1319 filename = g_build_filename (rb_user_data_dir (),
1320 "audioscrobbler",
1321 "stations",
1322 rb_audioscrobbler_service_get_name (page->priv->service),
1323 rb_audioscrobbler_account_get_username (page->priv->account),
1324 NULL);
1325
1326 uri = g_filename_to_uri (filename, NULL, NULL);
1327 error = NULL;
1328 rb_uri_create_parent_dirs (uri, &error);
1329 json_generator_to_file (generator, filename, NULL);
1330
1331 json_node_free (root);
1332 g_object_unref (generator);
1333 g_free (filename);
1334 g_free (uri);
1335 }
1336
1337 /* adds a new radio station for the user, if it doesn't already exist */
1338 static RBSource *
1339 add_radio_station (RBAudioscrobblerProfilePage *page,
1340 const char *url,
1341 const char *name)
1342 {
1343 GList *i;
1344 RBSource *radio = NULL;
1345
1346 /* check for existing station */
1347 for (i = page->priv->radio_sources; i != NULL; i = i->next) {
1348 char *existing_url;
1349 g_object_get (i->data, "station-url", &existing_url, NULL);
1350
1351 if (strcmp (existing_url, url) == 0) {
1352 radio = i->data;
1353 }
1354
1355 g_free (existing_url);
1356 }
1357
1358 if (radio == NULL) {
1359 const char *username;
1360 const char *session_key;
1361 RBShell *shell;
1362
1363 username = rb_audioscrobbler_account_get_username (page->priv->account);
1364 session_key = rb_audioscrobbler_account_get_session_key (page->priv->account);
1365 g_object_get (page, "shell", &shell, NULL);
1366
1367 radio = rb_audioscrobbler_radio_source_new (page,
1368 page->priv->service,
1369 username,
1370 session_key,
1371 name,
1372 url);
1373 page->priv->radio_sources = g_list_append (page->priv->radio_sources, radio);
1374 g_signal_connect (radio, "notify::name",
1375 G_CALLBACK (radio_station_name_changed_cb),
1376 page);
1377 save_radio_stations (page);
1378
1379 g_object_unref (shell);
1380 }
1381
1382 return radio;
1383 }
1384
1385 /* called when a radio station's name changes */
1386 static void
1387 radio_station_name_changed_cb (RBAudioscrobblerRadioSource *radio,
1388 GParamSpec *spec,
1389 RBAudioscrobblerProfilePage *page)
1390 {
1391 /* save list of stations with new name */
1392 save_radio_stations (page);
1393 }
1394
1395 /* removes a station from user's list of radio stations, deletes the source */
1396 void
1397 rb_audioscrobbler_profile_page_remove_radio_station (RBAudioscrobblerProfilePage *page,
1398 RBSource *station)
1399 {
1400 GList *i;
1401
1402 i = g_list_find (page->priv->radio_sources, station);
1403
1404 if (i != NULL) {
1405 rb_display_page_delete_thyself (i->data);
1406 page->priv->radio_sources = g_list_remove (page->priv->radio_sources, i->data);
1407 save_radio_stations (page);
1408 }
1409 }
1410
1411 static gboolean
1412 update_timeout_cb (RBAudioscrobblerProfilePage *page)
1413 {
1414 rb_audioscrobbler_user_update (page->priv->user);
1415
1416 return TRUE;
1417 }
1418
1419 static void
1420 user_info_updated_cb (RBAudioscrobblerUser *user,
1421 RBAudioscrobblerUserData *data,
1422 RBAudioscrobblerProfilePage *page)
1423 {
1424 if (data != NULL) {
1425 char *playcount_text;
1426
1427 gtk_label_set_label (GTK_LABEL (page->priv->username_label),
1428 data->user_info.username);
1429 gtk_widget_show (page->priv->username_label);
1430
1431 playcount_text = g_strdup_printf (_("%s plays"), data->user_info.playcount);
1432 gtk_label_set_label (GTK_LABEL (page->priv->playcount_label),
1433 playcount_text);
1434 g_free (playcount_text);
1435 gtk_widget_show (page->priv->playcount_label);
1436
1437 gtk_link_button_set_uri (GTK_LINK_BUTTON (page->priv->view_profile_link),
1438 data->url);
1439 gtk_widget_show (page->priv->view_profile_link);
1440
1441 if (data->image != NULL) {
1442 gtk_image_set_from_pixbuf (GTK_IMAGE (page->priv->profile_image), data->image);
1443 /* show the parent because the image is packed in a viewport so it has a shadow */
1444 gtk_widget_show (gtk_widget_get_parent (page->priv->profile_image));
1445 } else {
1446 gtk_widget_hide (gtk_widget_get_parent (page->priv->profile_image));
1447 }
1448 } else {
1449 gtk_widget_hide (page->priv->playcount_label);
1450 gtk_widget_hide (page->priv->view_profile_link);
1451 gtk_widget_hide (gtk_widget_get_parent (page->priv->profile_image));
1452 }
1453 }
1454
1455 static void
1456 recent_tracks_updated_cb (RBAudioscrobblerUser *user,
1457 GPtrArray *recent_tracks,
1458 RBAudioscrobblerProfilePage *page)
1459 {
1460 set_user_list (page, page->priv->recent_tracks_wrap_box, recent_tracks);
1461
1462 if (recent_tracks != NULL && recent_tracks->len != 0) {
1463 gtk_widget_show_all (page->priv->recent_tracks_area);
1464 } else {
1465 gtk_widget_hide (page->priv->recent_tracks_area);
1466 }
1467 }
1468
1469 static void
1470 top_tracks_updated_cb (RBAudioscrobblerUser *user,
1471 GPtrArray *top_tracks,
1472 RBAudioscrobblerProfilePage *page)
1473 {
1474 set_user_list (page, page->priv->top_tracks_wrap_box, top_tracks);
1475
1476 if (top_tracks != NULL && top_tracks->len != 0) {
1477 gtk_widget_show_all (page->priv->top_tracks_area);
1478 } else {
1479 gtk_widget_hide (page->priv->top_tracks_area);
1480 }
1481 }
1482
1483 static void
1484 loved_tracks_updated_cb (RBAudioscrobblerUser *user,
1485 GPtrArray *loved_tracks,
1486 RBAudioscrobblerProfilePage *page)
1487 {
1488 set_user_list (page, page->priv->loved_tracks_wrap_box, loved_tracks);
1489
1490 if (loved_tracks != NULL && loved_tracks->len != 0) {
1491 gtk_widget_show_all (page->priv->loved_tracks_area);
1492 } else {
1493 gtk_widget_hide (page->priv->loved_tracks_area);
1494 }
1495 }
1496
1497 static void
1498 top_artists_updated_cb (RBAudioscrobblerUser *user,
1499 GPtrArray *top_artists,
1500 RBAudioscrobblerProfilePage *page)
1501 {
1502 set_user_list (page, page->priv->top_artists_wrap_box, top_artists);
1503
1504 if (top_artists != NULL && top_artists->len != 0) {
1505 gtk_widget_show_all (page->priv->top_artists_area);
1506 } else {
1507 gtk_widget_hide (page->priv->top_artists_area);
1508 }
1509 }
1510
1511 static void
1512 recommended_artists_updated_cb (RBAudioscrobblerUser *user,
1513 GPtrArray *recommended_artists,
1514 RBAudioscrobblerProfilePage *page)
1515 {
1516 set_user_list (page, page->priv->recommended_artists_wrap_box, recommended_artists);
1517
1518 if (recommended_artists != NULL && recommended_artists->len != 0) {
1519 gtk_widget_show_all (page->priv->recommended_artists_area);
1520 } else {
1521 gtk_widget_hide (page->priv->recommended_artists_area);
1522 }
1523 }
1524
1525 /* Creates a list of buttons packed in a wrap box for a list of data
1526 * eg user's top tracks or recommended artists
1527 */
1528 static void
1529 set_user_list (RBAudioscrobblerProfilePage *page,
1530 GtkWidget *list_wrap_box,
1531 GPtrArray *list_data)
1532 {
1533 GList *button_node;
1534
1535 /* delete all existing buttons */
1536 for (button_node = gtk_container_get_children (GTK_CONTAINER (list_wrap_box));
1537 button_node != NULL;
1538 button_node = g_list_next (button_node)) {
1539 GtkMenu *menu;
1540 menu = g_hash_table_lookup (page->priv->button_to_popup_menu_map, button_node->data);
1541 g_hash_table_remove (page->priv->button_to_popup_menu_map, button_node->data);
1542 g_hash_table_remove (page->priv->popup_menu_to_data_map, menu);
1543 gtk_widget_destroy (button_node->data);
1544 }
1545
1546 if (list_data != NULL) {
1547 int i;
1548 int max_image_width;
1549
1550 /* get the width of the widest image */
1551 max_image_width = 0;
1552 for (i = 0; i < list_data->len; i++) {
1553 RBAudioscrobblerUserData *data;
1554
1555 data = g_ptr_array_index (list_data, i);
1556 if (data->image != NULL) {
1557 int width = gdk_pixbuf_get_width (data->image);
1558 max_image_width = MAX (max_image_width, width);
1559 }
1560
1561 }
1562
1563 /* add a new button for each item in the list */
1564 for (i = 0; i < list_data->len; i++) {
1565 RBAudioscrobblerUserData *data;
1566 GtkWidget *button;
1567 GtkWidget *menu;
1568
1569 data = g_ptr_array_index (list_data, i);
1570 button = create_list_button (page, data, max_image_width);
1571 menu = create_popup_menu (page, data);
1572
1573 g_hash_table_insert (page->priv->button_to_popup_menu_map, button, g_object_ref_sink (menu));
1574 g_hash_table_insert (page->priv->popup_menu_to_data_map, menu, data);
1575
1576 egg_wrap_box_insert_child (EGG_WRAP_BOX (list_wrap_box),
1577 button,
1578 -1,
1579 EGG_WRAP_BOX_H_EXPAND);
1580 }
1581 }
1582 }
1583
1584 /* creates a button for use in a list */
1585 static GtkWidget *
1586 create_list_button (RBAudioscrobblerProfilePage *page,
1587 RBAudioscrobblerUserData *data,
1588 int max_sibling_image_width)
1589 {
1590 GtkWidget *button;
1591 GtkWidget *button_contents;
1592 char *button_markup;
1593 int label_indent;
1594 GtkWidget *label;
1595 GtkWidget *label_alignment;
1596
1597 button = gtk_button_new ();
1598 gtk_button_set_alignment (GTK_BUTTON (button),
1599 0, 0.5);
1600 gtk_button_set_focus_on_click (GTK_BUTTON (button),
1601 FALSE);
1602 gtk_button_set_relief (GTK_BUTTON (button),
1603 GTK_RELIEF_NONE);
1604
1605 button_contents = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
1606 gtk_container_add (GTK_CONTAINER (button), button_contents);
1607
1608 if (data->image != NULL) {
1609 GtkWidget *image;
1610 GtkWidget *viewport;
1611 GtkWidget *alignment;
1612
1613 image = gtk_image_new_from_pixbuf (data->image);
1614
1615 viewport = gtk_viewport_new (NULL, NULL);
1616 gtk_container_add (GTK_CONTAINER (viewport), image);
1617
1618 alignment = gtk_alignment_new (0, 0.5, 0, 0);
1619 gtk_container_add (GTK_CONTAINER (alignment), viewport);
1620
1621 gtk_box_pack_start (GTK_BOX (button_contents),
1622 alignment,
1623 FALSE, FALSE, 0);
1624
1625 label_indent = max_sibling_image_width - gdk_pixbuf_get_width (data->image);
1626 } else {
1627 label_indent = 4;
1628 }
1629
1630 button_markup = NULL;
1631 if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK) {
1632 char *escaped_title_text;
1633 char *escaped_artist_text;
1634
1635 escaped_title_text = g_markup_escape_text (data->track.title, -1);
1636 escaped_artist_text = g_markup_escape_text (data->track.artist, -1);
1637 button_markup = g_strdup_printf ("%s\n<small>%s</small>",
1638 escaped_title_text,
1639 escaped_artist_text);
1640
1641 g_free (escaped_title_text);
1642 g_free (escaped_artist_text);
1643
1644 } else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
1645 button_markup = g_markup_escape_text (data->artist.name, -1);
1646 }
1647
1648 label = gtk_label_new ("");
1649 gtk_label_set_markup (GTK_LABEL (label), button_markup);
1650 g_free (button_markup);
1651
1652 label_alignment = gtk_alignment_new (0, 0.5, 0, 0);
1653 gtk_container_add (GTK_CONTAINER (label_alignment), label);
1654
1655 gtk_alignment_set_padding (GTK_ALIGNMENT (label_alignment),
1656 0, 0,
1657 label_indent, 0);
1658
1659 gtk_box_pack_start (GTK_BOX (button_contents),
1660 label_alignment,
1661 FALSE, FALSE, 0);
1662
1663 g_signal_connect (button,
1664 "clicked",
1665 G_CALLBACK (list_item_clicked_cb),
1666 page);
1667
1668 return button;
1669 }
1670
1671 /* creates a menu to be popped up when a button is clicked */
1672 static GtkWidget *
1673 create_popup_menu (RBAudioscrobblerProfilePage *page,
1674 RBAudioscrobblerUserData *data)
1675 {
1676 GtkWidget *menu;
1677
1678 menu = gtk_menu_new ();
1679
1680 /* Visit on website */
1681 if (data->url != NULL && data->url[0] != '\0') {
1682 GtkWidget *view_url_item;
1683 char *item_text;
1684
1685 /* Translators: %s is the name of the audioscrobbler service, for example "Last.fm".
1686 * This is the label for menu item which when activated will take the user to the
1687 * artist/track's page on the service's website. */
1688 item_text = g_strdup_printf (_("_View on %s"),
1689 rb_audioscrobbler_service_get_name (page->priv->service));
1690 view_url_item = gtk_menu_item_new_with_mnemonic (item_text);
1691 g_signal_connect (view_url_item,
1692 "activate",
1693 G_CALLBACK (list_item_view_url_activated_cb),
1694 page);
1695
1696 gtk_menu_shell_append (GTK_MENU_SHELL (menu), view_url_item);
1697 g_free (item_text);
1698 }
1699
1700 /* Similar artists radio */
1701 if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK ||
1702 data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
1703 GtkWidget *similar_artists_item;
1704
1705 similar_artists_item = gtk_menu_item_new_with_mnemonic (_("Listen to _Similar Artists Radio"));
1706 g_signal_connect (similar_artists_item,
1707 "activate",
1708 G_CALLBACK (list_item_listen_similar_artists_activated_cb),
1709 page);
1710
1711 gtk_menu_shell_append (GTK_MENU_SHELL (menu), similar_artists_item);
1712 }
1713
1714 /* Top fans radio */
1715 if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK ||
1716 data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
1717 GtkWidget *top_fans_item;
1718
1719 top_fans_item = gtk_menu_item_new_with_mnemonic (_("Listen to _Top Fans Radio"));
1720 g_signal_connect (top_fans_item,
1721 "activate",
1722 G_CALLBACK (list_item_listen_top_fans_activated_cb),
1723 page);
1724
1725 gtk_menu_shell_append (GTK_MENU_SHELL (menu), top_fans_item);
1726 }
1727
1728 gtk_widget_show_all (menu);
1729
1730 return menu;
1731 }
1732
1733 /* popup the appropriate menu */
1734 static void
1735 list_item_clicked_cb (GtkButton *button, RBAudioscrobblerProfilePage *page)
1736 {
1737 GtkWidget *menu;
1738
1739 menu = g_hash_table_lookup (page->priv->button_to_popup_menu_map, button);
1740
1741 /* show menu if it has any items in it */
1742 if (g_list_length (gtk_container_get_children (GTK_CONTAINER (menu))) != 0) {
1743 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
1744 }
1745 }
1746
1747 static void
1748 list_item_view_url_activated_cb (GtkMenuItem *menuitem,
1749 RBAudioscrobblerProfilePage *page)
1750 {
1751 GtkWidget *menu;
1752 RBAudioscrobblerUserData *data;
1753
1754 menu = gtk_widget_get_parent (GTK_WIDGET (menuitem));
1755 data = g_hash_table_lookup (page->priv->popup_menu_to_data_map, menu);
1756
1757 /* some urls are given to us without the http:// prefix */
1758 if (g_str_has_prefix (data->url, "http://") == TRUE) {
1759 gtk_show_uri (NULL, data->url, GDK_CURRENT_TIME, NULL);
1760 } else {
1761 char *url;
1762 url = g_strdup_printf ("%s%s", "http://", data->url);
1763 gtk_show_uri (NULL, url, GDK_CURRENT_TIME, NULL);
1764 g_free (url);
1765 }
1766 }
1767
1768 static void
1769 list_item_listen_similar_artists_activated_cb (GtkMenuItem *menuitem,
1770 RBAudioscrobblerProfilePage *page)
1771 {
1772 GtkWidget *menu;
1773 RBAudioscrobblerUserData *data;
1774 const char *artist = NULL;
1775 char *radio_url;
1776 char *radio_name;
1777 RBSource *radio;
1778 RBShell *shell;
1779 RBDisplayPageTree *page_tree;
1780
1781 menu = gtk_widget_get_parent (GTK_WIDGET (menuitem));
1782 data = g_hash_table_lookup (page->priv->popup_menu_to_data_map, menu);
1783 if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
1784 artist = data->artist.name;
1785 } else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK) {
1786 artist = data->track.artist;
1787 }
1788
1789 radio_url = g_strdup_printf (rb_audioscrobbler_radio_type_get_url (RB_AUDIOSCROBBLER_RADIO_TYPE_SIMILAR_ARTISTS),
1790 artist);
1791 radio_name = g_strdup_printf (rb_audioscrobbler_radio_type_get_default_name (RB_AUDIOSCROBBLER_RADIO_TYPE_SIMILAR_ARTISTS),
1792 artist);
1793
1794 radio = add_radio_station (page, radio_url, radio_name);
1795 g_object_get (page, "shell", &shell, NULL);
1796 g_object_get (shell, "display-page-tree", &page_tree, NULL);
1797 rb_display_page_tree_select (page_tree, RB_DISPLAY_PAGE (radio));
1798
1799 g_free (radio_url);
1800 g_free (radio_name);
1801 g_object_unref (shell);
1802 g_object_unref (page_tree);
1803 }
1804
1805 static void
1806 list_item_listen_top_fans_activated_cb (GtkMenuItem *menuitem,
1807 RBAudioscrobblerProfilePage *page)
1808 {
1809 GtkWidget *menu;
1810 RBAudioscrobblerUserData *data;
1811 const char *artist = NULL;
1812 char *radio_url;
1813 char *radio_name;
1814 RBSource *radio;
1815 RBShell *shell;
1816 RBDisplayPageTree *page_tree;
1817
1818 menu = gtk_widget_get_parent (GTK_WIDGET (menuitem));
1819 data = g_hash_table_lookup (page->priv->popup_menu_to_data_map, menu);
1820 if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
1821 artist = data->artist.name;
1822 } else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK) {
1823 artist = data->track.artist;
1824 }
1825
1826 radio_url = g_strdup_printf (rb_audioscrobbler_radio_type_get_url (RB_AUDIOSCROBBLER_RADIO_TYPE_TOP_FANS),
1827 artist);
1828 radio_name = g_strdup_printf (rb_audioscrobbler_radio_type_get_default_name (RB_AUDIOSCROBBLER_RADIO_TYPE_TOP_FANS),
1829 artist);
1830
1831 radio = add_radio_station (page, radio_url, radio_name);
1832 g_object_get (page, "shell", &shell, NULL);
1833 g_object_get (shell, "display-page-tree", &page_tree, NULL);
1834 rb_display_page_tree_select (page_tree, RB_DISPLAY_PAGE (radio));
1835
1836 g_free (radio_url);
1837 g_free (radio_name);
1838 g_object_unref (shell);
1839 g_object_unref (page_tree);
1840 }
1841
1842 static void
1843 impl_selected (RBDisplayPage *bpage)
1844 {
1845 RBAudioscrobblerProfilePage *page = RB_AUDIOSCROBBLER_PROFILE_PAGE (bpage);
1846
1847 RB_DISPLAY_PAGE_CLASS (rb_audioscrobbler_profile_page_parent_class)->selected (bpage);
1848
1849 /* attempt to update now and again every 5 minutes */
1850 rb_audioscrobbler_user_update (page->priv->user);
1851 page->priv->update_timeout_id = g_timeout_add_seconds (300,
1852 (GSourceFunc) update_timeout_cb,
1853 page);
1854 }
1855
1856 static void
1857 impl_deselected (RBDisplayPage *bpage)
1858 {
1859 RBAudioscrobblerProfilePage *page = RB_AUDIOSCROBBLER_PROFILE_PAGE (bpage);
1860
1861 RB_DISPLAY_PAGE_CLASS (rb_audioscrobbler_profile_page_parent_class)->deselected (bpage);
1862
1863 g_source_remove (page->priv->update_timeout_id);
1864 page->priv->update_timeout_id = 0;
1865 }
1866
1867 static gboolean
1868 impl_show_popup (RBDisplayPage *page)
1869 {
1870 _rb_display_page_show_popup (page, AUDIOSCROBBLER_PROFILE_PAGE_POPUP_PATH);
1871 return TRUE;
1872 }
1873
1874 static void
1875 impl_delete_thyself (RBDisplayPage *bpage)
1876 {
1877 RBAudioscrobblerProfilePage *page;
1878 GList *i;
1879 GtkUIManager *ui_manager;
1880
1881 rb_debug ("deleting profile page");
1882
1883 page = RB_AUDIOSCROBBLER_PROFILE_PAGE (bpage);
1884
1885 for (i = page->priv->radio_sources; i != NULL; i = i->next) {
1886 rb_display_page_delete_thyself (i->data);
1887 }
1888
1889 g_object_get (page, "ui-manager", &ui_manager, NULL);
1890 gtk_ui_manager_remove_ui (ui_manager, page->priv->ui_merge_id);
1891 gtk_ui_manager_remove_action_group (ui_manager, page->priv->service_action_group);
1892
1893 g_object_unref (ui_manager);
1894 }
1895
1896 void
1897 _rb_audioscrobbler_profile_page_register_type (GTypeModule *module)
1898 {
1899 rb_audioscrobbler_profile_page_register_type (module);
1900 }