[gitg/gtk3] details panel: show gravatar when changing the revision



commit 381075db01e70f1a4e3613a199e97740bd485881
Author: Ignacio Casal Quinteiro <icq gnome org>
Date:   Thu Jul 21 20:07:42 2011 +0200

    details panel: show gravatar when changing the revision

 gitg/Makefile.am                    |    2 +
 gitg/gitg-avatar-cache.c            |  371 +++++++++++++++++++++++++++++++++++
 gitg/gitg-avatar-cache.h            |   73 +++++++
 gitg/gitg-revision-details-panel.c  |   60 ++++++
 gitg/gitg-revision-details-panel.ui |  317 ++++++++++++++++++------------
 5 files changed, 698 insertions(+), 125 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 50dc17f..5358908 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -13,6 +13,7 @@ AM_CPPFLAGS =							\
 
 NOINST_H_FILES =			\
 	gitg-activatable.h		\
+	gitg-avatar-cache.h		\
 	gitg-branch-actions.h		\
 	gitg-cell-renderer-path.h	\
 	gitg-commit-view.h		\
@@ -36,6 +37,7 @@ gitg_SOURCES =				\
 	$(BUILT_SOURCES)		\
 	gitg.c				\
 	gitg-activatable.c		\
+	gitg-avatar-cache.c		\
 	gitg-branch-actions.c		\
 	gitg-cell-renderer-path.c	\
 	gitg-commit-view.c		\
diff --git a/gitg/gitg-avatar-cache.c b/gitg/gitg-avatar-cache.c
new file mode 100644
index 0000000..e95c78b
--- /dev/null
+++ b/gitg/gitg-avatar-cache.c
@@ -0,0 +1,371 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Mathias Hasselmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gitg-avatar-cache.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <string.h>
+
+#define AVATAR_SIZE 80
+
+typedef struct _GitgAvatarCacheLoader GitgAvatarCacheLoader;
+struct _GitgAvatarCacheLoader
+{
+	gchar              *uri;
+	GitgAvatarCache  *cache;
+	GCancellable       *cancellable;
+	gchar               buffer[8192];
+	GdkPixbufLoader    *pixbuf_loader;
+	GdkPixbuf          *pixbuf;
+	GSimpleAsyncResult *result;
+};
+
+struct _GitgAvatarCachePrivate
+{
+	GChecksum  *checksum;
+	GHashTable *pixbuf_table;
+	GList      *active_loaders;
+};
+
+G_DEFINE_TYPE (GitgAvatarCache, gitg_avatar_cache, G_TYPE_OBJECT)
+
+static void
+avatar_cache_loader_finish (GitgAvatarCacheLoader *loader,
+			    GError                  *error)
+{
+	if (loader->cache)
+	{
+		g_object_remove_weak_pointer (G_OBJECT (loader->cache),
+		                              (gpointer) &loader->cache);
+	}
+
+	if (loader->cancellable)
+	{
+		g_cancellable_cancel (loader->cancellable);
+		g_object_unref (loader->cancellable);
+	}
+
+	if (loader->pixbuf_loader)
+	{
+		gdk_pixbuf_loader_close (loader->pixbuf_loader, NULL);
+		g_object_unref (loader->pixbuf_loader);
+	}
+
+	if (loader->pixbuf)
+	{
+		g_simple_async_result_set_op_res_gpointer (loader->result,
+		                                           g_object_ref (loader->pixbuf),
+		                                           g_object_unref);
+	}
+	else
+	{
+		g_simple_async_result_set_from_error (loader->result, error);
+		g_error_free (error);
+	}
+
+	g_simple_async_result_complete_in_idle (loader->result);
+	g_object_unref (loader->result);
+
+	g_slice_free (GitgAvatarCacheLoader, loader);
+}
+
+static void
+gitg_avatar_cache_init (GitgAvatarCache *cache)
+{
+	cache->priv = G_TYPE_INSTANCE_GET_PRIVATE (cache,
+	                                           GITG_TYPE_AVATAR_CACHE,
+	                                           GitgAvatarCachePrivate);
+
+}
+
+static void
+avatar_cache_insert (GitgAvatarCache *cache,
+		     const gchar       *uri,
+		     GdkPixbuf         *pixbuf)
+{
+	GitgAvatarCachePrivate *priv = cache->priv;
+
+	if (!priv->pixbuf_table)
+	{
+		priv->pixbuf_table = g_hash_table_new_full (g_str_hash,
+		                                            g_str_equal,
+		                                            g_free,
+		                                            g_object_unref);
+	}
+
+	g_hash_table_insert (priv->pixbuf_table, g_strdup (uri),
+	                     g_object_ref (pixbuf));
+}
+
+static void
+avatar_cache_close_cb (GObject      *object,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+	GitgAvatarCacheLoader *loader = user_data;
+	GInputStream            *stream = G_INPUT_STREAM (object);
+	GError                  *error = NULL;
+
+	if (g_input_stream_close_finish (stream, result, &error) &&
+	    gdk_pixbuf_loader_close (loader->pixbuf_loader, &error))
+	{
+		loader->pixbuf = gdk_pixbuf_loader_get_pixbuf (loader->pixbuf_loader);
+		avatar_cache_insert (loader->cache, loader->uri, loader->pixbuf);
+		g_object_unref (loader->pixbuf_loader);
+		loader->pixbuf_loader = NULL;
+	}
+
+	if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		g_warning ("%s: %s", G_STRFUNC, error->message);
+
+	avatar_cache_loader_finish (loader, error);
+}
+
+static void
+avatar_cache_read_cb (GObject      *object,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+	GitgAvatarCacheLoader *loader = user_data;
+	GInputStream            *stream = G_INPUT_STREAM (object);
+	GError                  *error = NULL;
+	gssize                   len;
+
+	len = g_input_stream_read_finish (stream, result, &error);
+
+	if (len > 0)
+	{
+		if (gdk_pixbuf_loader_write (loader->pixbuf_loader, (gpointer)
+					    loader->buffer, len, &error))
+		{
+			g_input_stream_read_async (stream, loader->buffer,
+			                           sizeof (loader->buffer),
+			                           G_PRIORITY_DEFAULT,
+			                           loader->cancellable,
+			                           avatar_cache_read_cb,
+			                           loader);
+		}
+		else
+		{
+			len = -2;
+		}
+	}
+
+	if (0 >= len)
+	{
+		g_input_stream_close_async (stream, G_PRIORITY_DEFAULT,
+		                            loader->cancellable,
+		                            avatar_cache_close_cb, loader);
+	}
+
+	if (error)
+	{
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			g_warning ("%s: %s", G_STRFUNC, error->message);
+
+		g_error_free (error);
+	}
+}
+
+static void
+avatar_cache_open_cb (GObject      *object,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+	GitgAvatarCacheLoader *loader = user_data;
+	GError                  *error = NULL;
+	GFileInputStream        *stream;
+
+	stream = g_file_read_finish (G_FILE (object), result, &error);
+
+	if (stream)
+	{
+		g_input_stream_read_async (G_INPUT_STREAM (stream),
+		                           loader->buffer, sizeof (loader->buffer),
+		                           G_PRIORITY_DEFAULT, loader->cancellable,
+		                           avatar_cache_read_cb, loader);
+	}
+	else
+	{
+		if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			g_warning ("%s: %s", G_STRFUNC, error->message);
+
+		avatar_cache_loader_finish (loader, error);
+	}
+}
+
+static void
+avatar_cache_finalize (GObject *object)
+{
+	GitgAvatarCachePrivate *priv = GITG_AVATAR_CACHE (object)->priv;
+	GitgAvatarCacheLoader *loader;
+	GList                   *l;
+
+	if (priv->pixbuf_table)
+	{
+		g_hash_table_unref (priv->pixbuf_table);
+	}
+
+	if (priv->checksum)
+	{
+		g_checksum_free (priv->checksum);
+	}
+
+	for (l = priv->active_loaders; l; l = g_list_delete_link (l, l))
+	{
+		loader = priv->active_loaders->data;
+		g_cancellable_cancel (loader->cancellable);
+	}
+
+	G_OBJECT_CLASS (gitg_avatar_cache_parent_class)->finalize (object);
+}
+
+static void
+gitg_avatar_cache_class_init (GitgAvatarCacheClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->finalize     = avatar_cache_finalize;
+
+	g_type_class_add_private (class, sizeof (GitgAvatarCachePrivate));
+}
+
+GitgAvatarCache *
+gitg_avatar_cache_new (void)
+{
+	return g_object_new (GITG_TYPE_AVATAR_CACHE, NULL);
+}
+
+void
+gitg_avatar_cache_load_uri_async (GitgAvatarCache   *cache,
+                                  const gchar         *uri,
+                                  gint                 io_priority,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+	GitgAvatarCachePrivate *priv;
+	GdkPixbuf                *pixbuf = NULL;
+	GSimpleAsyncResult       *result;
+	GitgAvatarCacheLoader  *loader;
+	GFile                    *file;
+
+	g_return_if_fail (GITG_IS_AVATAR_CACHE (cache));
+	g_return_if_fail (NULL != callback);
+	g_return_if_fail (NULL != uri);
+
+	priv = cache->priv;
+
+	result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data,
+	                                    gitg_avatar_cache_load_uri_async);
+
+	if (priv->pixbuf_table)
+		pixbuf = g_hash_table_lookup (priv->pixbuf_table, uri);
+
+	if (pixbuf)
+	{
+		g_simple_async_result_set_op_res_gpointer (result,
+		                                           g_object_ref (pixbuf),
+		                                           g_object_unref);
+		g_simple_async_result_complete_in_idle (result);
+	}
+	else
+	{
+		if (cancellable)
+		{
+			g_object_ref (cancellable);
+		}
+		else
+		{
+			cancellable = g_cancellable_new ();
+		}
+
+		loader = g_slice_new0 (GitgAvatarCacheLoader);
+		loader->pixbuf_loader = gdk_pixbuf_loader_new ();
+		loader->result = g_object_ref (result);
+		loader->cancellable = cancellable;
+		loader->uri = g_strdup (uri);
+		loader->cache = cache;
+
+		g_object_add_weak_pointer (G_OBJECT (loader->cache),
+		                           (gpointer) &loader->cache);
+
+		file = g_file_new_for_uri (uri);
+
+		g_file_read_async (file, G_PRIORITY_DEFAULT, loader->cancellable,
+		                   avatar_cache_open_cb, loader);
+
+		g_object_unref (file);
+	}
+
+	g_object_unref (result);
+}
+
+GdkPixbuf *
+gitg_avatar_cache_load_finish (GitgAvatarCache  *cache,
+                               GAsyncResult       *result,
+                               GError            **error)
+{
+	GSimpleAsyncResult *simple;
+	gpointer            source_tag;
+
+	g_return_val_if_fail (GITG_IS_AVATAR_CACHE (cache), NULL);
+	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	source_tag = g_simple_async_result_get_source_tag (simple);
+
+	g_return_val_if_fail (source_tag == gitg_avatar_cache_load_uri_async, NULL);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	return g_simple_async_result_get_op_res_gpointer (simple);
+}
+
+gchar *
+gitg_avatar_cache_get_gravatar_uri (GitgAvatarCache   *cache,
+                                    const gchar         *gravatar_id)
+{
+	GitgAvatarCachePrivate *priv;
+
+	g_return_val_if_fail (GITG_IS_AVATAR_CACHE (cache), NULL);
+	g_return_val_if_fail (NULL != gravatar_id, NULL);
+
+	priv = cache->priv;
+
+	if (priv->checksum)
+	{
+		g_checksum_reset (priv->checksum);
+	}
+	else
+	{
+		priv->checksum = g_checksum_new (G_CHECKSUM_MD5);
+	}
+
+	g_checksum_update (priv->checksum, (gpointer) gravatar_id,
+	                   strlen (gravatar_id));
+
+	return g_strdup_printf ("http://www.gravatar.com/avatar/%s?s=%d";,
+	                        g_checksum_get_string (priv->checksum),
+	                        AVATAR_SIZE);
+}
diff --git a/gitg/gitg-avatar-cache.h b/gitg/gitg-avatar-cache.h
new file mode 100644
index 0000000..dca9a34
--- /dev/null
+++ b/gitg/gitg-avatar-cache.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Mathias Hasselmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GITG_AVATAR_CACHE_H__
+#define __GITG_AVATAR_CACHE_H__
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GITG_TYPE_AVATAR_CACHE            (gitg_avatar_cache_get_type ())
+#define GITG_AVATAR_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GITG_TYPE_AVATAR_CACHE, GitgAvatarCache))
+#define GITG_AVATAR_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GITG_TYPE_AVATAR_CACHE, GitgAvatarCacheClass))
+#define GITG_IS_AVATAR_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GITG_TYPE_AVATAR_CACHE))
+#define GITG_IS_AVATAR_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GITG_TYPE_AVATAR_CACHE))
+#define GITG_AVATAR_CACHE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GITG_TYPE_AVATAR_CACHE, GitgAvatarCacheClass))
+
+typedef struct _GitgAvatarCache        GitgAvatarCache;
+typedef struct _GitgAvatarCachePrivate GitgAvatarCachePrivate;
+typedef struct _GitgAvatarCacheClass   GitgAvatarCacheClass;
+
+struct _GitgAvatarCache
+{
+	GObject parent_instance;
+
+	GitgAvatarCachePrivate *priv;
+};
+
+struct _GitgAvatarCacheClass
+{
+	GObjectClass parent_class;
+};
+
+GType               gitg_avatar_cache_get_type         (void) G_GNUC_CONST;
+
+GitgAvatarCache    *gitg_avatar_cache_new              (void);
+
+void                gitg_avatar_cache_load_uri_async   (GitgAvatarCache   *cache,
+                                                        const gchar         *uri,
+                                                        gint                 io_priority,
+                                                        GCancellable        *cancellable,
+                                                        GAsyncReadyCallback  callback,
+                                                        gpointer             user_data);
+
+GdkPixbuf          *gitg_avatar_cache_load_finish      (GitgAvatarCache   *cache,
+                                                        GAsyncResult        *result,
+                                                        GError             **error);
+
+gchar              *gitg_avatar_cache_get_gravatar_uri (GitgAvatarCache   *cache,
+                                                        const gchar         *gravatar_id);
+
+G_END_DECLS
+
+#endif /* __GITG_AVATAR_CACHE_H__ */
+
diff --git a/gitg/gitg-revision-details-panel.c b/gitg/gitg-revision-details-panel.c
index 67b52da..5d0f6b6 100644
--- a/gitg/gitg-revision-details-panel.c
+++ b/gitg/gitg-revision-details-panel.c
@@ -26,6 +26,7 @@
 #include "gitg-revision-panel.h"
 #include "gitg-stat-view.h"
 #include "gitg-uri.h"
+#include "gitg-avatar-cache.h"
 
 #include <glib/gi18n.h>
 #include <stdlib.h>
@@ -46,6 +47,7 @@ struct _GitgRevisionDetailsPanelPrivate
 	GtkLabel *committer;
 	GtkLabel *subject;
 	GtkTable *parents;
+	GtkImage *avatar;
 
 	GtkWidget *panel_widget;
 	GtkTextView *text_view;
@@ -60,6 +62,8 @@ struct _GitgRevisionDetailsPanelPrivate
 
 	GSList *stats;
 	GitgWindow *window;
+
+	GitgAvatarCache *cache;
 };
 
 static void gitg_revision_panel_iface_init (GitgRevisionPanelInterface *iface);
@@ -125,6 +129,7 @@ initialize_ui (GitgRevisionDetailsPanel *panel)
 	priv->subject = GTK_LABEL (gtk_builder_get_object (priv->builder, "label_subject"));
 	priv->parents = GTK_TABLE (gtk_builder_get_object (priv->builder, "table_parents"));
 	priv->text_view = GTK_TEXT_VIEW (gtk_builder_get_object (priv->builder, "text_view_details"));
+	priv->avatar = GTK_IMAGE (gtk_builder_get_object (priv->builder, "image_avatar"));
 
 	gchar const *lbls[] = {
 		"label_subject_lbl",
@@ -211,6 +216,12 @@ gitg_revision_details_panel_dispose (GObject *object)
 		panel->priv->shell = NULL;
 	}
 
+	if (panel->priv->cache)
+	{
+		g_object_unref (panel->priv->cache);
+		panel->priv->cache = NULL;
+	}
+
 	G_OBJECT_CLASS (gitg_revision_details_panel_parent_class)->dispose (object);
 }
 static void
@@ -548,6 +559,8 @@ gitg_revision_details_panel_init (GitgRevisionDetailsPanel *self)
 	                  "update",
 	                  G_CALLBACK (on_shell_update),
 	                  self);
+
+	self->priv->cache = gitg_avatar_cache_new ();
 }
 
 #define HASH_KEY "GitgRevisionDetailsPanelHashKey"
@@ -726,6 +739,50 @@ update_details (GitgRevisionDetailsPanel *panel)
 }
 
 static void
+avatar_ready (GObject                  *source_object,
+              GAsyncResult             *res,
+              GitgRevisionDetailsPanel *panel)
+{
+	GdkPixbuf *pixbuf;
+	GError *error = NULL;
+
+	pixbuf = gitg_avatar_cache_load_finish (panel->priv->cache,
+	                                        res,
+	                                        &error);
+
+	if (error == NULL)
+	{
+		gtk_image_set_from_pixbuf (panel->priv->avatar,
+		                           pixbuf);
+		gtk_widget_show (GTK_WIDGET (panel->priv->avatar));
+	}
+}
+
+static void
+set_avatar (GitgRevisionDetailsPanel *panel,
+            const gchar              *email)
+{
+	if (email == NULL || *email == '\0')
+	{
+		gtk_widget_hide (GTK_WIDGET (panel->priv->avatar));
+	}
+	else
+	{
+		gchar *uri;
+
+		uri = gitg_avatar_cache_get_gravatar_uri (panel->priv->cache,
+		                                          email);
+		gitg_avatar_cache_load_uri_async (panel->priv->cache,
+		                                  uri,
+		                                  G_PRIORITY_DEFAULT,
+		                                  NULL,
+		                                  (GAsyncReadyCallback)avatar_ready,
+		                                  panel);
+		g_free (uri);
+	}
+}
+
+static void
 reload (GitgRevisionDetailsPanel *panel)
 {
 	GtkClipboard *cb;
@@ -775,6 +832,8 @@ reload (GitgRevisionDetailsPanel *panel)
 		gtk_clipboard_set_text (cb, sha, -1);
 
 		g_free (sha);
+
+		set_avatar (panel, gitg_revision_get_author_email (panel->priv->revision));
 	}
 	else
 	{
@@ -782,6 +841,7 @@ reload (GitgRevisionDetailsPanel *panel)
 		gtk_label_set_text (panel->priv->committer, "");
 		gtk_label_set_text (panel->priv->subject, "");
 		gtk_label_set_text (panel->priv->sha, "");
+		set_avatar (panel, NULL);
 	}
 
 	// Update parents
diff --git a/gitg/gitg-revision-details-panel.ui b/gitg/gitg-revision-details-panel.ui
index ff0cd79..85a988e 100644
--- a/gitg/gitg-revision-details-panel.ui
+++ b/gitg/gitg-revision-details-panel.ui
@@ -10,17 +10,177 @@
       <object class="GtkGrid" id="grid1">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="row_spacing">6</property>
-        <property name="column_spacing">6</property>
-        <property name="n_rows">5</property>
-        <property name="n_columns">2</property>
         <child>
-          <object class="GtkLabel" id="label_sha_lbl">
+          <object class="GtkGrid" id="grid2">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="xalign">1</property>
-            <property name="label" translatable="yes">SHA:</property>
-            <property name="use_markup">True</property>
+            <property name="hexpand">True</property>
+            <property name="row_spacing">6</property>
+            <property name="column_spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="label_sha_lbl">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">SHA:</property>
+                <property name="use_markup">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_author_lbl">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Author:</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_committer_lbl">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Committer:</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_subject_lbl">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Subject:</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_parent_lbl">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="yalign">0</property>
+                <property name="label" translatable="yes">Parent:</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTable" id="table_parents">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">3</property>
+                <property name="row_spacing">2</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_subject">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="use_markup">True</property>
+                <property name="selectable">True</property>
+                <property name="ellipsize">end</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_committer">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="use_markup">True</property>
+                <property name="selectable">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_author">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="use_markup">True</property>
+                <property name="selectable">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">1</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label_sha">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="use_markup">True</property>
+                <property name="selectable">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -30,144 +190,51 @@
           </packing>
         </child>
         <child>
-          <object class="GtkLabel" id="label_author_lbl">
+          <object class="GtkBox" id="box1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="xalign">1</property>
-            <property name="label" translatable="yes">Author:</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkImage" id="image_avatar">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="stock">gtk-missing-image</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
           </object>
           <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">1</property>
+            <property name="left_attach">1</property>
+            <property name="top_attach">0</property>
             <property name="width">1</property>
             <property name="height">1</property>
           </packing>
         </child>
         <child>
-          <object class="GtkLabel" id="label_committer_lbl">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">1</property>
-            <property name="label" translatable="yes">Committer:</property>
-          </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">2</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
+          <placeholder/>
         </child>
         <child>
-          <object class="GtkLabel" id="label_subject_lbl">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">1</property>
-            <property name="label" translatable="yes">Subject:</property>
-          </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">3</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
+          <placeholder/>
         </child>
         <child>
-          <object class="GtkLabel" id="label_parent_lbl">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">1</property>
-            <property name="yalign">0</property>
-            <property name="label" translatable="yes">Parent:</property>
-          </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">4</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
+          <placeholder/>
         </child>
         <child>
-          <object class="GtkTable" id="table_parents">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="hexpand">True</property>
-            <property name="n_columns">2</property>
-            <property name="column_spacing">3</property>
-            <property name="row_spacing">2</property>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-          </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="top_attach">4</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkLabel" id="label_subject">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">0</property>
-            <property name="use_markup">True</property>
-            <property name="selectable">True</property>
-            <property name="ellipsize">end</property>
-          </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="top_attach">3</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
+          <placeholder/>
         </child>
         <child>
-          <object class="GtkLabel" id="label_committer">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">0</property>
-            <property name="use_markup">True</property>
-            <property name="selectable">True</property>
-          </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="top_attach">2</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
+          <placeholder/>
         </child>
         <child>
-          <object class="GtkLabel" id="label_author">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">0</property>
-            <property name="use_markup">True</property>
-            <property name="selectable">True</property>
-          </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="top_attach">1</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
+          <placeholder/>
         </child>
         <child>
-          <object class="GtkLabel" id="label_sha">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">0</property>
-            <property name="use_markup">True</property>
-            <property name="selectable">True</property>
-          </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="top_attach">0</property>
-            <property name="width">1</property>
-            <property name="height">1</property>
-          </packing>
+          <placeholder/>
         </child>
       </object>
       <packing>



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