[rhythmbox] audioscrobbler: display some of user's listening stats in profile source



commit d4a03eba3d6fdbf263cd31e764ead3932bf0bebe
Author: Jamie Nicol <jamie thenicols net>
Date:   Thu Jun 24 01:23:51 2010 +0100

    audioscrobbler: display some of user's listening stats in profile source
    
    Display lists of recent tracks, top tracks, loved tracks, top artists
    and recommended artists, aswell as username and a link to their profile
    page.

 plugins/audioscrobbler/audioscrobbler-profile.ui   |  514 ++++++++++++++++++++
 .../rb-audioscrobbler-profile-source.c             |  490 +++++++++++++++++--
 2 files changed, 959 insertions(+), 45 deletions(-)
---
diff --git a/plugins/audioscrobbler/audioscrobbler-profile.ui b/plugins/audioscrobbler/audioscrobbler-profile.ui
new file mode 100644
index 0000000..2d9411e
--- /dev/null
+++ b/plugins/audioscrobbler/audioscrobbler-profile.ui
@@ -0,0 +1,514 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkVBox" id="profile_vbox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">10</property>
+    <child>
+      <object class="GtkHBox" id="user_info_area">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkImage" id="profile_image">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkHBox" id="hbox3">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkLabel" id="username_label">
+                    <property name="visible">True</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                      <attribute name="scale" value="2.000000"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="logout_button">
+                    <property name="label" translatable="yes">Logout</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="relief">half</property>
+                    <signal name="clicked" handler="rb_audioscrobbler_profile_source_logout_button_clicked_cb"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="padding">5</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="xscale">0</property>
+                <child>
+                  <object class="GtkLinkButton" id="view_profile_link">
+                    <property name="label" translatable="yes">View your profile</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="relief">none</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="padding">8</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="recent_tracks_area">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHSeparator" id="hseparator1">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="recent_tracks_label">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="xpad">10</property>
+            <property name="ypad">10</property>
+            <property name="label" translatable="yes">Recently Listened Tracks</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLayout" id="layout1">
+            <property name="visible">True</property>
+            <property name="width">0</property>
+            <property name="height">0</property>
+            <signal name="size_allocate" handler="rb_audioscrobbler_profile_source_list_layout_size_allocate_cb"/>
+            <child>
+              <object class="GtkTable" id="recent_tracks_table">
+                <property name="visible">True</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="top_artists_area">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHSeparator" id="hseparator2">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="top_artists_label">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="xpad">10</property>
+            <property name="ypad">10</property>
+            <property name="label" translatable="yes">Top Artists</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLayout" id="layout2">
+            <property name="visible">True</property>
+            <property name="width">0</property>
+            <property name="height">0</property>
+            <signal name="size_allocate" handler="rb_audioscrobbler_profile_source_list_layout_size_allocate_cb"/>
+            <child>
+              <object class="GtkTable" id="top_artists_table">
+                <property name="visible">True</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="recommended_artists_area">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHSeparator" id="hseparator3">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="recommended_artists_label">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="xpad">10</property>
+            <property name="ypad">10</property>
+            <property name="label" translatable="yes">Recommendations</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLayout" id="layout3">
+            <property name="visible">True</property>
+            <property name="width">0</property>
+            <property name="height">0</property>
+            <signal name="size_allocate" handler="rb_audioscrobbler_profile_source_list_layout_size_allocate_cb"/>
+            <child>
+              <object class="GtkTable" id="recommended_artists_table">
+                <property name="visible">True</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="top_tracks_area">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHSeparator" id="hseparator4">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="top_tracks_label">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="xpad">10</property>
+            <property name="ypad">10</property>
+            <property name="label" translatable="yes">Top Tracks</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLayout" id="layout4">
+            <property name="visible">True</property>
+            <property name="width">0</property>
+            <property name="height">0</property>
+            <signal name="size_allocate" handler="rb_audioscrobbler_profile_source_list_layout_size_allocate_cb"/>
+            <child>
+              <object class="GtkTable" id="top_tracks_table">
+                <property name="visible">True</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">4</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="loved_tracks_area">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHSeparator" id="hseparator5">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="loved_tracks_label">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="xpad">10</property>
+            <property name="ypad">10</property>
+            <property name="label" translatable="yes">Loved Tracks</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLayout" id="layout5">
+            <property name="visible">True</property>
+            <property name="width">0</property>
+            <property name="height">0</property>
+            <signal name="size_allocate" handler="rb_audioscrobbler_profile_source_list_layout_size_allocate_cb"/>
+            <child>
+              <object class="GtkTable" id="loved_tracks_table">
+                <property name="visible">True</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">5</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c b/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c
index 4ff0d91..815a43a 100644
--- a/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c
+++ b/plugins/audioscrobbler/rb-audioscrobbler-profile-source.c
@@ -27,11 +27,17 @@
  */
 
 #include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <math.h>
 
 #include "rb-audioscrobbler-profile-source.h"
 #include "rb-audioscrobbler.h"
 #include "rb-audioscrobbler-account.h"
+#include "rb-audioscrobbler-user.h"
 #include "rb-debug.h"
+#include "rb-builder-helpers.h"
+#include "rb-file-helpers.h"
 #include "rb-util.h"
 
 struct _RBAudioscrobblerProfileSourcePrivate {
@@ -39,11 +45,31 @@ struct _RBAudioscrobblerProfileSourcePrivate {
 	RBAudioscrobblerAccount *account;
 	RBAudioscrobbler *audioscrobbler;
 
+	RBAudioscrobblerUser *user;
+
 	GtkWidget *main_vbox;
 
 	GtkWidget *login_bar;
 	GtkWidget *login_status_label;
 	GtkWidget *login_response_button;
+
+	GtkWidget *profile_vbox;
+
+	GtkWidget *user_info_area;
+	GtkWidget *profile_image;
+	GtkWidget *username_label;
+	GtkWidget *view_profile_link;
+
+	GtkWidget *recent_tracks_area;
+	GtkWidget *recent_tracks_table;
+	GtkWidget *top_tracks_area;
+	GtkWidget *top_tracks_table;
+	GtkWidget *loved_tracks_area;
+	GtkWidget *loved_tracks_table;
+	GtkWidget *top_artists_area;
+	GtkWidget *top_artists_table;
+	GtkWidget *recommended_artists_area;
+	GtkWidget *recommended_artists_table;
 };
 
 #define RB_AUDIOSCROBBLER_PROFILE_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_AUDIOSCROBBLER_PROFILE_SOURCE, RBAudioscrobblerProfileSourcePrivate))
@@ -63,9 +89,13 @@ static void rb_audioscrobbler_profile_source_set_property (GObject *object,
                                                            GParamSpec *pspec);
 
 static void rb_audioscrobbler_profile_source_init_login_ui (RBAudioscrobblerProfileSource *source);
+static void rb_audioscrobbler_profile_source_init_profile_ui (RBAudioscrobblerProfileSource *source);
+
 static void rb_audioscrobbler_profile_source_login_bar_response (GtkInfoBar *info_bar,
                                                                  gint response_id,
                                                                  gpointer user_data);
+void rb_audioscrobbler_profile_source_logout_button_clicked_cb (GtkButton *button,
+                                                                gpointer user_data);
 
 static void rb_audioscrobbler_profile_source_login_status_change_cb (RBAudioscrobblerAccount *account,
                                                                      RBAudioscrobblerAccountLoginStatus status,
@@ -74,6 +104,33 @@ static void rb_audioscrobbler_profile_source_login_status_change_cb (RBAudioscro
 static void rb_audioscrobbler_profile_source_scrobbler_authentication_error_cb (RBAudioscrobbler *audioscrobbler,
                                                                                 gpointer user_data);
 
+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);
+static void rb_audioscrobbler_profile_source_top_tracks_updated_cb (RBAudioscrobblerUser *user,
+                                                                    GPtrArray *top_tracks,
+                                                                    gpointer user_data);
+static void rb_audioscrobbler_profile_source_loved_tracks_updated_cb (RBAudioscrobblerUser *user,
+                                                                      GPtrArray *loved_tracks,
+                                                                      gpointer user_data);
+static void rb_audioscrobbler_profile_source_top_artists_updated_cb (RBAudioscrobblerUser *user,
+                                                                     GPtrArray *top_artists,
+                                                                     gpointer user_data);
+static void rb_audioscrobbler_profile_source_recommended_artists_updated_cb (RBAudioscrobblerUser *user,
+                                                                             GPtrArray *recommended_artists,
+                                                                             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
@@ -152,13 +209,6 @@ static void
 rb_audioscrobbler_profile_source_init (RBAudioscrobblerProfileSource *source)
 {
 	source->priv = RB_AUDIOSCROBBLER_PROFILE_SOURCE_GET_PRIVATE (source);
-
-	/* create the UI */
-	source->priv->main_vbox = gtk_vbox_new (FALSE, 4);
-	gtk_box_pack_start (GTK_BOX (source), source->priv->main_vbox, TRUE, TRUE, 0);
-	gtk_widget_show_all (source->priv->main_vbox);
-
-	rb_audioscrobbler_profile_source_init_login_ui (source);
 }
 
 static void
@@ -166,21 +216,21 @@ rb_audioscrobbler_profile_source_constructed (GObject *object)
 {
 	RBAudioscrobblerProfileSource *source;
 	RBShell *shell;
-	RBAudioscrobblerAccountLoginStatus login_status;
 
 	RB_CHAIN_GOBJECT_METHOD (rb_audioscrobbler_profile_source_parent_class, constructed, object);
 
 	source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (object);
 	g_object_get (source, "shell", &shell, NULL);
 
-	/* create the account */
-	source->priv->account = rb_audioscrobbler_account_new (source->priv->service);
-	g_signal_connect (source->priv->account,
-	                  "login-status-changed",
-	                  (GCallback)rb_audioscrobbler_profile_source_login_status_change_cb,
-	                  source);
+	/* create the UI */
+	source->priv->main_vbox = gtk_vbox_new (FALSE, 4);
+	gtk_box_pack_start (GTK_BOX (source), source->priv->main_vbox, TRUE, TRUE, 0);
+	gtk_widget_show (source->priv->main_vbox);
 
-	/* create the scrobbler */
+	rb_audioscrobbler_profile_source_init_login_ui (source);
+	rb_audioscrobbler_profile_source_init_profile_ui (source);
+
+	/* create the scrobbler - submits listening data */
 	source->priv->audioscrobbler =
 		rb_audioscrobbler_new (source->priv->service,
 		                       RB_SHELL_PLAYER (rb_shell_get_player (shell)));
@@ -189,10 +239,41 @@ rb_audioscrobbler_profile_source_constructed (GObject *object)
 	                  (GCallback)rb_audioscrobbler_profile_source_scrobbler_authentication_error_cb,
 	                  source);
 
-	/* sync account settings to UI and scrobbler settings */
-	g_object_get (source->priv->account, "login-status", &login_status, NULL);
+	/* create the user */
+	source->priv->user = rb_audioscrobbler_user_new (source->priv->service);
+	g_signal_connect (source->priv->user,
+	                  "user-info-updated",
+	                  (GCallback)rb_audioscrobbler_profile_source_user_info_updated_cb,
+	                  source);
+	g_signal_connect (source->priv->user,
+	                  "recent-tracks-updated",
+	                  (GCallback)rb_audioscrobbler_profile_source_recent_tracks_updated_cb,
+	                  source);
+	g_signal_connect (source->priv->user,
+	                  "top-tracks-updated",
+	                  (GCallback)rb_audioscrobbler_profile_source_top_tracks_updated_cb,
+	                  source);
+	g_signal_connect (source->priv->user,
+	                  "loved-tracks-updated",
+	                  (GCallback)rb_audioscrobbler_profile_source_loved_tracks_updated_cb,
+	                  source);
+	g_signal_connect (source->priv->user,
+	                  "top-artists-updated",
+	                  (GCallback)rb_audioscrobbler_profile_source_top_artists_updated_cb,
+	                  source);
+	g_signal_connect (source->priv->user,
+	                  "recommended-artists-updated",
+	                  (GCallback)rb_audioscrobbler_profile_source_recommended_artists_updated_cb,
+	                  source);
+
+	/* create the account */
+	source->priv->account = rb_audioscrobbler_account_new (source->priv->service);
+	g_signal_connect (source->priv->account,
+	                  "login-status-changed",
+	                  (GCallback)rb_audioscrobbler_profile_source_login_status_change_cb,
+	                  source);
 	rb_audioscrobbler_profile_source_login_status_change_cb (source->priv->account,
-	                                                         login_status,
+	                                                         rb_audioscrobbler_account_get_login_status (source->priv->account),
 	                                                         source);
 
 	g_object_unref (shell);
@@ -220,6 +301,11 @@ rb_audioscrobbler_profile_source_dispose (GObject* object)
 		source->priv->account = NULL;
 	}
 
+	if (source->priv->user != NULL) {
+		g_object_unref (source->priv->user);
+		source->priv->user = NULL;
+	}
+
 	G_OBJECT_CLASS (rb_audioscrobbler_profile_source_parent_class)->dispose (object);
 }
 
@@ -227,7 +313,6 @@ static void
 rb_audioscrobbler_profile_source_finalize (GObject *object)
 {
 	RBAudioscrobblerProfileSource *source;
-
 	source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (object);
 
 	G_OBJECT_CLASS (rb_audioscrobbler_profile_source_parent_class)->finalize (object);
@@ -284,24 +369,78 @@ rb_audioscrobbler_profile_source_init_login_ui (RBAudioscrobblerProfileSource *s
 }
 
 static void
+rb_audioscrobbler_profile_source_init_profile_ui (RBAudioscrobblerProfileSource *source)
+{
+	RBPlugin *plugin;
+	char *builder_file;
+	GtkBuilder *builder;
+	GtkWidget *viewport;
+	GtkWidget *scrolled_win;
+
+	g_object_get (source, "plugin", &plugin, NULL);
+
+	builder_file = rb_plugin_find_file (plugin, "audioscrobbler-profile.ui");
+	g_assert (builder_file != NULL);
+	builder = rb_builder_load (builder_file, source);
+
+	source->priv->profile_vbox = GTK_WIDGET (gtk_builder_get_object (builder, "profile_vbox"));
+
+	source->priv->user_info_area = GTK_WIDGET (gtk_builder_get_object (builder, "user_info_area"));
+	source->priv->profile_image = GTK_WIDGET (gtk_builder_get_object (builder, "profile_image"));
+	source->priv->username_label = GTK_WIDGET (gtk_builder_get_object (builder, "username_label"));
+	source->priv->view_profile_link = GTK_WIDGET (gtk_builder_get_object (builder, "view_profile_link"));
+
+	source->priv->recent_tracks_area = GTK_WIDGET (gtk_builder_get_object (builder, "recent_tracks_area"));
+	source->priv->recent_tracks_table = GTK_WIDGET (gtk_builder_get_object (builder, "recent_tracks_table"));
+
+	source->priv->top_tracks_area = GTK_WIDGET (gtk_builder_get_object (builder, "top_tracks_area"));
+	source->priv->top_tracks_table = GTK_WIDGET (gtk_builder_get_object (builder, "top_tracks_table"));
+
+	source->priv->loved_tracks_area = GTK_WIDGET (gtk_builder_get_object (builder, "loved_tracks_area"));
+	source->priv->loved_tracks_table = GTK_WIDGET (gtk_builder_get_object (builder, "loved_tracks_table"));
+
+	source->priv->top_artists_area = GTK_WIDGET (gtk_builder_get_object (builder, "top_artists_area"));
+	source->priv->top_artists_table = GTK_WIDGET (gtk_builder_get_object (builder, "top_artists_table"));
+
+	source->priv->recommended_artists_area = GTK_WIDGET (gtk_builder_get_object (builder, "recommended_artists_area"));
+	source->priv->recommended_artists_table = GTK_WIDGET (gtk_builder_get_object (builder, "recommended_artists_table"));
+
+
+	viewport = gtk_viewport_new (NULL, NULL);
+	gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport),
+	                              GTK_SHADOW_NONE);
+	scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+	                                     GTK_SHADOW_NONE);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+	                                GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	gtk_container_add (GTK_CONTAINER (scrolled_win),
+	                   viewport);
+	gtk_container_add (GTK_CONTAINER (viewport),
+	                   source->priv->profile_vbox);
+	gtk_widget_show_all (scrolled_win);
+	gtk_box_pack_start (GTK_BOX (source->priv->main_vbox), scrolled_win, TRUE, TRUE, 0);
+
+
+	g_object_unref (plugin);
+	g_free (builder_file);
+	g_object_unref (builder);
+}
+
+static void
 rb_audioscrobbler_profile_source_login_bar_response (GtkInfoBar *info_bar,
                                                      gint response_id,
                                                      gpointer user_data)
 {
-	RBAudioscrobblerProfileSource *source;
-	RBAudioscrobblerAccountLoginStatus status;
-
-	source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
-
-	g_object_get (source->priv->account, "login-status", &status, NULL);
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
 
-	switch (status) {
+	switch (rb_audioscrobbler_account_get_login_status (source->priv->account)) {
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_OUT:
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_AUTH_ERROR:
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_CONNECTION_ERROR:
 		rb_audioscrobbler_account_authenticate (source->priv->account);
 		break;
-	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGING_IN:	
+	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGING_IN:
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_IN:
 		rb_audioscrobbler_account_logout (source->priv->account);
 		break;
@@ -310,55 +449,75 @@ rb_audioscrobbler_profile_source_login_bar_response (GtkInfoBar *info_bar,
 		break;
 	}
 }
+
+void
+rb_audioscrobbler_profile_source_logout_button_clicked_cb (GtkButton *button,
+                                                           gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+
+	rb_audioscrobbler_account_logout (source->priv->account);
+}
+
 static void
 rb_audioscrobbler_profile_source_login_status_change_cb (RBAudioscrobblerAccount *account,
                                                          RBAudioscrobblerAccountLoginStatus status,
                                                          gpointer user_data)
 {
 	RBAudioscrobblerProfileSource *source;
-	char *username;
-	char *session_key;
-	char *label_text;
-	char *button_text;
+	const char *username;
+	const char *session_key;
+	char *label_text = NULL;
+	char *button_text = NULL;
+	gboolean show_login_bar;
+	gboolean show_profile;
 
 	source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
 
-	g_object_get (account,
-	              "username", &username,
-	              "session-key", &session_key,
-	              NULL);
+	username = rb_audioscrobbler_account_get_username (source->priv->account);
+	session_key = rb_audioscrobbler_account_get_session_key (source->priv->account);
 
+	/* update the audioscrobbler with new authentication */
 	rb_audioscrobbler_set_authentication_details (source->priv->audioscrobbler,
 	                                              username,
 	                                              session_key);
 
+	/* set the new user details */
+	rb_audioscrobbler_user_set_authentication_details (source->priv->user, username, session_key);
+	if (username != NULL) {
+		rb_audioscrobbler_user_update (source->priv->user);
+	}
+
+	/* update the login ui */
 	switch (status) {
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_OUT:
-		gtk_widget_show_all (source->priv->login_bar);
+		show_login_bar = TRUE;
+		show_profile = FALSE;
 		label_text = g_strdup (_("You are not currently logged in."));
 		button_text = g_strdup (_("Login"));
 		gtk_info_bar_set_message_type (GTK_INFO_BAR (source->priv->login_bar), GTK_MESSAGE_INFO);
 		break;
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGING_IN:
-		gtk_widget_show_all (source->priv->login_bar);
+		show_login_bar = TRUE;
+		show_profile = FALSE;
 		label_text = g_strdup (_("Waiting for authentication..."));
 		button_text = g_strdup (_("Cancel"));
 		gtk_info_bar_set_message_type (GTK_INFO_BAR (source->priv->login_bar), GTK_MESSAGE_INFO);
 		break;
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_LOGGED_IN:
-		gtk_widget_show_all (source->priv->login_bar);
-		label_text = g_strdup_printf (_("Logged in as %s."), username);
-		button_text = g_strdup (_("Logout"));
-		gtk_info_bar_set_message_type (GTK_INFO_BAR (source->priv->login_bar), GTK_MESSAGE_INFO);
+		show_login_bar = FALSE;
+		show_profile = TRUE;
 		break;
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_AUTH_ERROR:
-		gtk_widget_show_all (source->priv->login_bar);
+		show_login_bar = TRUE;
+		show_profile = FALSE;
 		label_text = g_strdup (_("Authentication error. Please try logging in again."));
 		button_text = g_strdup (_("Login"));
 		gtk_info_bar_set_message_type (GTK_INFO_BAR (source->priv->login_bar), GTK_MESSAGE_WARNING);
 		break;
 	case RB_AUDIOSCROBBLER_ACCOUNT_LOGIN_STATUS_CONNECTION_ERROR:
-		gtk_widget_show_all (source->priv->login_bar);
+		show_login_bar = TRUE;
+		show_profile = FALSE;
 		label_text = g_strdup (_("Connection error. Please try logging in again."));
 		button_text = g_strdup (_("Login"));
 		gtk_info_bar_set_message_type (GTK_INFO_BAR (source->priv->login_bar), GTK_MESSAGE_WARNING);
@@ -370,9 +529,17 @@ rb_audioscrobbler_profile_source_login_status_change_cb (RBAudioscrobblerAccount
 
 	gtk_label_set_label (GTK_LABEL (source->priv->login_status_label), label_text);
 	gtk_button_set_label (GTK_BUTTON (source->priv->login_response_button), button_text);
+	if (show_login_bar == TRUE) {
+		gtk_widget_show_all (source->priv->login_bar);
+	} else {
+		gtk_widget_hide_all (source->priv->login_bar);
+	}
+	if (show_profile == TRUE) {
+		gtk_widget_show (source->priv->profile_vbox);
+	} else {
+		gtk_widget_hide_all (source->priv->profile_vbox);
+	}
 
-	g_free (username);
-	g_free (session_key);
 	g_free (label_text);
 	g_free (button_text);
 }
@@ -385,3 +552,236 @@ rb_audioscrobbler_profile_source_scrobbler_authentication_error_cb (RBAudioscrob
 
 	rb_audioscrobbler_account_notify_of_auth_error (source->priv->account);
 }
+
+static void
+rb_audioscrobbler_profile_source_set_user_list (RBAudioscrobblerProfileSource *source,
+                                                GtkWidget *list_table,
+                                                GPtrArray *list_data)
+{
+	int i;
+	GList *button_node;
+
+	gtk_table_resize (GTK_TABLE (list_table), list_data->len, 1);
+
+	/* 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 = NULL;
+		char *escaped_title_text = NULL;
+		char *escaped_artist_text = NULL;
+
+		button = gtk_button_new_with_label ("");
+		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);
+
+		rb_audioscrobbler_profile_source_list_table_pack_start (GTK_TABLE (list_table), button);
+
+		data = g_ptr_array_index (list_data, i);
+
+		if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK) {
+			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 ("%i. %s\n<small>%s</small>",
+				                       i + 1,
+				                       escaped_title_text,
+				                       escaped_artist_text);
+			gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (button))),
+				              button_text);
+
+		} else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
+			button_text = g_strdup_printf ("%i. %s",
+				                       i + 1,
+				                       data->artist.name);
+			gtk_button_set_label (GTK_BUTTON (button), button_text);
+		}
+
+		g_free (escaped_title_text);
+		g_free (escaped_artist_text);
+		g_free (button_text);
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_user_info_updated_cb (RBAudioscrobblerUser *user,
+                                                       RBAudioscrobblerUserData *data,
+                                                       gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+	if (data != NULL) {
+		gtk_label_set_label (GTK_LABEL (source->priv->username_label),
+			             data->user_info.username);
+		gtk_link_button_set_uri (GTK_LINK_BUTTON (source->priv->view_profile_link),
+		                         data->user_info.url);
+		gtk_image_set_from_pixbuf (GTK_IMAGE (source->priv->profile_image), data->user_info.image);
+
+		gtk_widget_show_all (source->priv->user_info_area);
+	} else {
+		gtk_label_set_label (GTK_LABEL (source->priv->username_label),
+			             "");
+		gtk_image_set_from_pixbuf (GTK_IMAGE (source->priv->profile_image), NULL);
+
+		gtk_widget_hide_all (source->priv->user_info_area);
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_recent_tracks_updated_cb (RBAudioscrobblerUser *user,
+                                                           GPtrArray *recent_tracks,
+                                                           gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+
+	if (recent_tracks != NULL) {
+		rb_audioscrobbler_profile_source_set_user_list (source, source->priv->recent_tracks_table, recent_tracks);
+		gtk_widget_show_all (source->priv->recent_tracks_area);
+	} else {
+		gtk_widget_hide_all (source->priv->recent_tracks_area);
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_top_tracks_updated_cb (RBAudioscrobblerUser *user,
+                                                        GPtrArray *top_tracks,
+                                                        gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+
+	if (top_tracks != NULL) {
+		rb_audioscrobbler_profile_source_set_user_list (source, source->priv->top_tracks_table, top_tracks);
+		gtk_widget_show_all (source->priv->top_tracks_area);
+	} else {
+		gtk_widget_hide_all (source->priv->top_tracks_area);
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_loved_tracks_updated_cb (RBAudioscrobblerUser *user,
+                                                          GPtrArray *loved_tracks,
+                                                          gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+
+	if (loved_tracks != NULL) {
+		rb_audioscrobbler_profile_source_set_user_list (source, source->priv->loved_tracks_table, loved_tracks);
+		gtk_widget_show_all (source->priv->loved_tracks_area);
+	} else {
+		gtk_widget_hide_all (source->priv->loved_tracks_area);
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_top_artists_updated_cb (RBAudioscrobblerUser *user,
+                                                         GPtrArray *top_artists,
+                                                         gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+
+	if (top_artists != NULL) {
+		rb_audioscrobbler_profile_source_set_user_list (source, source->priv->top_artists_table, top_artists);
+		gtk_widget_show_all (source->priv->top_artists_area);
+	} else {
+		gtk_widget_hide_all (source->priv->top_artists_area);
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_recommended_artists_updated_cb (RBAudioscrobblerUser *user,
+                                                                 GPtrArray *recommended_artists,
+                                                                 gpointer user_data)
+{
+	RBAudioscrobblerProfileSource *source = RB_AUDIOSCROBBLER_PROFILE_SOURCE (user_data);
+
+	if (recommended_artists != NULL) {
+		rb_audioscrobbler_profile_source_set_user_list (source, source->priv->recommended_artists_table, recommended_artists);
+		gtk_widget_show_all (source->priv->recommended_artists_area);
+	} else {
+		gtk_widget_hide_all (source->priv->recommended_artists_area);
+	}
+}
+
+static void
+rb_audioscrobbler_profile_source_list_table_pack_start (GtkTable *list_table, GtkWidget *item)
+{
+	int num_columns;
+	int num_rows;
+	int i;
+
+	g_object_get (list_table, "n-columns", &num_columns, "n-rows", &num_rows, NULL);
+	i = g_list_length (gtk_container_get_children (GTK_CONTAINER (list_table)));
+
+	gtk_table_attach_defaults (list_table,
+	                           item,
+	                           i % num_columns, i % num_columns + 1,
+	                           i / num_columns, i / num_columns + 1);
+}
+
+void
+rb_audioscrobbler_profile_source_list_layout_size_allocate_cb (GtkWidget *layout,
+                                                               GtkAllocation *allocation,
+                                                               gpointer user_data)
+{
+	GtkWidget *table = gtk_container_get_children (GTK_CONTAINER (layout))->data;
+	GList *buttons = gtk_container_get_children (GTK_CONTAINER (table));
+	int num_buttons;
+	GtkAllocation button_allocation;
+	int current_num_columns;
+	int new_num_columns;
+	int spacing;
+	GtkRequisition table_requisition;
+
+	num_buttons = g_list_length (buttons);
+	if (num_buttons == 0)
+		return;
+
+	gtk_widget_get_allocation (buttons->data, &button_allocation);
+
+	g_object_get (table, "n-columns", &current_num_columns, NULL);
+	spacing = gtk_table_get_default_col_spacing (GTK_TABLE (table));
+
+	new_num_columns = allocation->width / (button_allocation.width + spacing);
+	if (new_num_columns == 0) {
+		new_num_columns = 1;
+	}
+
+	if (new_num_columns != current_num_columns) {
+		int new_num_rows;
+		GList *button;
+
+		new_num_rows = (double)ceil ((double)num_buttons / (double)new_num_columns);
+
+		/* remove each button from the table, reffing it first so that it is not destroyed */
+		for (button = g_list_first (buttons); button != NULL; button = g_list_next (button)) {
+			g_object_ref (button->data);
+			gtk_container_remove (GTK_CONTAINER (table), button->data);
+		}
+
+		/* resize the table */
+		rb_debug ("resizing table from %i to %i columns", current_num_columns, new_num_columns);
+		gtk_table_resize (GTK_TABLE (table), new_num_columns, new_num_rows);
+
+		/* don't know why, but g_table_resize doesn't always update these properties properly */
+		g_object_set (table, "n-columns", new_num_columns, "n-rows", new_num_rows, NULL);
+
+		/* re-attach each button to the table */
+		for (button = g_list_last (buttons); button != NULL; button = g_list_previous (button)) {
+
+			rb_audioscrobbler_profile_source_list_table_pack_start (GTK_TABLE (table),
+			                                                        button->data);
+			g_object_unref (button->data);
+		}
+	}
+
+	gtk_widget_get_requisition (table, &table_requisition);
+	gtk_widget_set_size_request (layout, 0, table_requisition.height);
+	gtk_layout_set_size (GTK_LAYOUT (layout), table_requisition.width, table_requisition.height);
+}



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