[gitg] Added stat to revision details panel



commit 042917d2750e6002b2734f520cb7c5d64b7c567c
Author: Jesse van den Kieboom <jessevdk gnome org>
Date:   Sun Jun 6 20:59:28 2010 +0200

    Added stat to revision details panel

 gitg/Makefile.am                    |    2 +
 gitg/gitg-revision-changes-panel.c  |    6 +-
 gitg/gitg-revision-details-panel.c  |  320 ++++++++++++++++++++++
 gitg/gitg-revision-details-panel.ui |   25 ++-
 gitg/gitg-stat-view.c               |  510 +++++++++++++++++++++++++++++++++++
 gitg/gitg-stat-view.h               |   37 +++
 gitg/gitg-window.c                  |   16 +-
 gitg/gitg.c                         |   25 ++
 8 files changed, 936 insertions(+), 5 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 8156966..b063236 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -29,6 +29,7 @@ NOINST_H_FILES = 			\
 	gitg-revision-files-panel.h	\
 	gitg-revision-changes-panel.h	\
 	gitg-settings.h			\
+	gitg-stat-view.h		\
 	gitg-utils.h			\
 	gitg-window.h			\
 	gseal-gtk-compat.h
@@ -53,6 +54,7 @@ gitg_SOURCES = 				\
 	gitg-revision-files-panel.c	\
 	gitg-revision-changes-panel.c	\
 	gitg-settings.c			\
+	gitg-stat-view.c		\
 	gitg-utils.c			\
 	gitg-window.c			\
 	$(NOINST_H_FILES)
diff --git a/gitg/gitg-revision-changes-panel.c b/gitg/gitg-revision-changes-panel.c
index 8689233..681adaa 100644
--- a/gitg/gitg-revision-changes-panel.c
+++ b/gitg/gitg-revision-changes-panel.c
@@ -504,7 +504,7 @@ reload_diff (GitgRevisionChangesPanel *changes_panel)
 			                              "diff",
 			                              "--cached",
 			                              "-M",
-			                              "--pretty=format:%s%n%n%b",
+			                              "--pretty=format:",
 			                              "--encoding=UTF-8",
 			                              "--no-color",
 			                              NULL);
@@ -515,7 +515,7 @@ reload_diff (GitgRevisionChangesPanel *changes_panel)
 			                              NULL,
 			                              "diff",
 			                              "-M",
-			                              "--pretty=format:%s%n%n%b",
+			                              "--pretty=format:",
 			                              "--encoding=UTF-8",
 			                              "--no-color",
 			                              NULL);
@@ -528,7 +528,7 @@ reload_diff (GitgRevisionChangesPanel *changes_panel)
 			                              NULL,
 			                              "show",
 			                              "-M",
-			                              "--pretty=format:%s%n%n%b",
+			                              "--pretty=format:",
 			                              "--encoding=UTF-8",
 			                              "--no-color",
 			                              hash,
diff --git a/gitg/gitg-revision-details-panel.c b/gitg/gitg-revision-details-panel.c
index fc79f14..982d78e 100644
--- a/gitg/gitg-revision-details-panel.c
+++ b/gitg/gitg-revision-details-panel.c
@@ -24,11 +24,20 @@
 #include "gitg-revision-details-panel.h"
 #include "gitg-utils.h"
 #include "gitg-revision-panel.h"
+#include "gitg-stat-view.h"
 
 #include <glib/gi18n.h>
+#include <stdlib.h>
 
 #define GITG_REVISION_DETAILS_PANEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GITG_TYPE_REVISION_DETAILS_PANEL, GitgRevisionDetailsPanelPrivate))
 
+typedef struct
+{
+	gchar *file;
+	guint added;
+	guint removed;
+} StatInfo;
+
 struct _GitgRevisionDetailsPanelPrivate
 {
 	GtkLabel *sha;
@@ -38,10 +47,17 @@ struct _GitgRevisionDetailsPanelPrivate
 	GtkTable *parents;
 
 	GtkWidget *panel_widget;
+	GtkTextView *text_view;
+
 	GtkBuilder *builder;
 
 	GitgRepository *repository;
 	GitgRevision *revision;
+
+	GitgRunner *runner;
+	gboolean in_stat;
+
+	GSList *stats;
 };
 
 static void gitg_revision_panel_iface_init (GitgRevisionPanelInterface *iface);
@@ -100,6 +116,7 @@ initialize_ui (GitgRevisionDetailsPanel *panel)
 	priv->date = GTK_LABEL (gtk_builder_get_object (priv->builder, "label_date"));
 	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"));
 
 	gchar const *lbls[] = {
 		"label_subject_lbl",
@@ -169,6 +186,14 @@ gitg_revision_details_panel_dispose (GObject *object)
 		panel->priv->builder = NULL;
 	}
 
+	if (panel->priv->runner)
+	{
+		gitg_runner_cancel (panel->priv->runner);
+		g_object_unref (panel->priv->runner);
+
+		panel->priv->runner = NULL;
+	}
+
 	G_OBJECT_CLASS (gitg_revision_details_panel_parent_class)->dispose (object);
 }
 static void
@@ -183,10 +208,274 @@ gitg_revision_details_panel_class_init (GitgRevisionDetailsPanelClass *klass)
 }
 
 static void
+on_runner_begin (GitgRunner               *runner,
+                 GitgRevisionDetailsPanel *panel)
+{
+	GdkCursor *cursor;
+
+	cursor = gdk_cursor_new (GDK_WATCH);
+	gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (panel->priv->text_view)),
+	                       cursor);
+
+	panel->priv->in_stat = FALSE;
+
+	gdk_cursor_unref (cursor);
+}
+
+static void
+make_stats_table (GitgRevisionDetailsPanel *panel)
+{
+	guint num;
+	GtkTable *table;
+	GSList *item;
+	guint i;
+	guint max_lines = 0;
+	GtkTextChildAnchor *anchor;
+	GtkTextBuffer *buffer;
+	GtkTextIter iter;
+	gchar *path;
+	gchar *repo_uri;
+	gchar *sha1;
+	GFile *work_tree;
+
+	if (!panel->priv->stats)
+	{
+		return;
+	}
+
+	num = g_slist_length (panel->priv->stats);
+	table = GTK_TABLE (gtk_table_new (num, 3, FALSE));
+	gtk_table_set_row_spacings (table, 3);
+	gtk_table_set_col_spacings (table, 6);
+
+	for (item = panel->priv->stats; item; item = g_slist_next (item))
+	{
+		StatInfo *info = item->data;
+		guint total = info->added + info->removed;
+
+		if (total > max_lines)
+		{
+			max_lines = total;
+		}
+	}
+
+	item = panel->priv->stats;
+	work_tree = gitg_repository_get_work_tree (panel->priv->repository);
+	path = g_file_get_path (work_tree);
+	sha1 = gitg_revision_get_sha1 (panel->priv->revision);
+
+	g_object_unref (work_tree);
+
+	repo_uri = g_strdup_printf ("gitg://%s:%s", path, sha1);
+
+	g_free (sha1);
+	g_free (path);
+
+	for (i = 0; i < num; ++i)
+	{
+		StatInfo *info = item->data;
+		GtkWidget *view;
+		GtkWidget *file;
+		GtkWidget *total;
+		GtkWidget *align;
+		gchar *total_str;
+		gchar *uri;
+
+		view = gitg_stat_view_new (info->added,
+		                           info->removed,
+		                           max_lines);
+
+		align = gtk_alignment_new (0, 0.5, 1, 0);
+		gtk_widget_set_size_request (view, 300, 18);
+
+		gtk_container_add (GTK_CONTAINER (align), view);
+
+		uri = g_strdup_printf ("%s/changes/%s", repo_uri, info->file);
+
+		file = gtk_link_button_new_with_label (uri,
+		                                       info->file);
+
+		g_free (uri);
+
+		gtk_button_set_alignment (GTK_BUTTON (file),
+		                          0,
+		                          0.5);
+
+		total_str = g_strdup_printf ("%d", info->added + info->removed);
+		total = gtk_label_new (total_str);
+		g_free (total_str);
+
+		g_free (info->file);
+		g_slice_free (StatInfo, info);
+
+		gtk_table_attach (table, file,
+		                  0, 1, i, i + 1,
+		                  GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL,
+		                  0, 0);
+
+		gtk_table_attach (table, align,
+		                  1, 2, i, i + 1,
+		                  GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL,
+		                  0, 0);
+
+		gtk_table_attach (table, total,
+		                  2, 3, i, i + 1,
+		                  GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL,
+		                  0, 0);
+
+		gtk_widget_show (view);
+		gtk_widget_show (file);
+		gtk_widget_show (total);
+		gtk_widget_show (align);
+
+		item = g_slist_next (item);
+	}
+
+	gtk_widget_show (GTK_WIDGET (table));
+
+	buffer = gtk_text_view_get_buffer (panel->priv->text_view);
+
+	gtk_text_buffer_get_end_iter (buffer, &iter);
+	gtk_text_buffer_insert (buffer, &iter, "\n\n", 2);
+
+	anchor = gtk_text_buffer_create_child_anchor (buffer,
+	                                              &iter);
+
+	gtk_text_view_add_child_at_anchor (panel->priv->text_view,
+	                                   GTK_WIDGET (table),
+	                                   anchor);
+}
+
+static void
+on_runner_end (GitgRunner               *runner,
+               gboolean                  cancelled,
+               GitgRevisionDetailsPanel *panel)
+{
+	gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (panel->priv->text_view)),
+	                       NULL);
+
+	panel->priv->stats = g_slist_reverse (panel->priv->stats);
+
+	make_stats_table (panel);
+
+	g_slist_free (panel->priv->stats);
+	panel->priv->stats = NULL;
+}
+
+static void
+strip_trailing_newlines (GtkTextBuffer *buffer)
+{
+	GtkTextIter iter;
+	GtkTextIter end;
+
+	gtk_text_buffer_get_end_iter (buffer, &iter);
+
+	if (!gtk_text_iter_starts_line (&iter))
+	{
+		return;
+	}
+
+	while (!gtk_text_iter_is_start (&iter) &&
+	        gtk_text_iter_ends_line (&iter))
+	{
+		if (!gtk_text_iter_backward_line (&iter))
+		{
+			break;
+		}
+	}
+
+	gtk_text_iter_forward_to_line_end (&iter);
+
+	gtk_text_buffer_get_end_iter (buffer, &end);
+	gtk_text_buffer_delete (buffer, &iter, &end);
+}
+
+static void
+add_stat (GitgRevisionDetailsPanel *panel,
+          gchar const              *line)
+{
+	gchar **parts;
+
+	parts = g_strsplit_set (line, "\t ", -1);
+
+	if (g_strv_length (parts) == 3)
+	{
+		StatInfo *stat;
+
+		stat = g_slice_new (StatInfo);
+
+		stat->added = (guint)atoi (parts[0]);
+		stat->removed = (guint)atoi (parts[1]);
+		stat->file = g_strdup (parts[2]);
+
+		panel->priv->stats = g_slist_prepend (panel->priv->stats,
+		                                      stat);
+	}
+
+	g_strfreev (parts);
+}
+
+static void
+on_runner_update (GitgRunner                *runner,
+                  gchar                    **lines,
+                  GitgRevisionDetailsPanel  *panel)
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter end;
+
+	buffer = gtk_text_view_get_buffer (panel->priv->text_view);
+	gtk_text_buffer_get_end_iter (buffer, &end);
+
+	while (lines && *lines)
+	{
+		gchar const *line = *lines;
+		++lines;
+
+		if (panel->priv->in_stat)
+		{
+			add_stat (panel, line);
+		}
+		else
+		{
+			if (!gtk_text_iter_is_start (&end))
+			{
+				gtk_text_buffer_insert (buffer, &end, "\n", 1);
+			}
+
+			if (line[0] == '\x01' && !line[1])
+			{
+				panel->priv->in_stat = TRUE;
+				strip_trailing_newlines (buffer);
+			}
+			else
+			{
+				gtk_text_buffer_insert (buffer, &end, line, -1);
+			}
+		}
+	}
+}
+
+static void
 gitg_revision_details_panel_init (GitgRevisionDetailsPanel *self)
 {
 	self->priv = GITG_REVISION_DETAILS_PANEL_GET_PRIVATE(self);
 
+	self->priv->runner = gitg_runner_new (1000);
+
+	g_signal_connect (self->priv->runner,
+	                  "begin-loading",
+	                  G_CALLBACK (on_runner_begin),
+	                  self);
+
+	g_signal_connect (self->priv->runner,
+	                  "end-loading",
+	                  G_CALLBACK (on_runner_end),
+	                  self);
+
+	g_signal_connect (self->priv->runner,
+	                  "update",
+	                  G_CALLBACK (on_runner_update),
+	                  self);
 }
 
 #define HASH_KEY "GitgRevisionDetailsPanelHashKey"
@@ -333,6 +622,36 @@ update_parents (GitgRevisionDetailsPanel *self)
 }
 
 static void
+update_details (GitgRevisionDetailsPanel *panel)
+{
+	gchar *sha1;
+
+	gitg_runner_cancel (panel->priv->runner);
+
+	gtk_text_buffer_set_text (gtk_text_view_get_buffer (panel->priv->text_view),
+	                          "",
+	                          0);
+
+	if (!panel->priv->revision)
+	{
+		return;
+	}
+
+	sha1 = gitg_revision_get_sha1 (panel->priv->revision);
+
+	gitg_repository_run_commandv (panel->priv->repository,
+	                              panel->priv->runner,
+	                              NULL,
+	                              "show",
+	                              "--numstat",
+	                              "--pretty=format:%s%n%n%b%n\x01",
+	                              sha1,
+	                              NULL);
+
+	g_free (sha1);
+}
+
+static void
 reload (GitgRevisionDetailsPanel *panel)
 {
 	GtkClipboard *cb;
@@ -373,6 +692,7 @@ reload (GitgRevisionDetailsPanel *panel)
 
 	// Update parents
 	update_parents (panel);
+	update_details (panel);
 }
 
 static void
diff --git a/gitg/gitg-revision-details-panel.ui b/gitg/gitg-revision-details-panel.ui
index 0b2ce4a..103d7b0 100644
--- a/gitg/gitg-revision-details-panel.ui
+++ b/gitg/gitg-revision-details-panel.ui
@@ -1,5 +1,7 @@
 <?xml version="1.0"?>
 <interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy toplevel-contextual -->
   <object class="GtkVBox" id="revision_details_page">
     <property name="visible">True</property>
     <property name="spacing">3</property>
@@ -36,7 +38,7 @@
             <property name="xalign">0</property>
             <property name="use_markup">True</property>
             <property name="selectable">True</property>
-            <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+            <property name="ellipsize">end</property>
           </object>
           <packing>
             <property name="left_attach">1</property>
@@ -158,5 +160,26 @@
         <property name="position">0</property>
       </packing>
     </child>
+    <child>
+      <object class="GtkScrolledWindow" id="scrolled_window_details">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="hscrollbar_policy">automatic</property>
+        <property name="vscrollbar_policy">automatic</property>
+        <property name="shadow_type">etched-in</property>
+        <child>
+          <object class="GtkTextView" id="text_view_details">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="editable">False</property>
+            <property name="cursor_visible">False</property>
+            <property name="accepts_tab">False</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="position">1</property>
+      </packing>
+    </child>
   </object>
 </interface>
diff --git a/gitg/gitg-stat-view.c b/gitg/gitg-stat-view.c
new file mode 100644
index 0000000..a04f9b2
--- /dev/null
+++ b/gitg/gitg-stat-view.c
@@ -0,0 +1,510 @@
+#include "gitg-stat-view.h"
+
+#include "gitg-utils.h"
+#include <math.h>
+#include <cairo.h>
+
+#define GITG_STAT_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GITG_TYPE_STAT_VIEW, GitgStatViewPrivate))
+
+enum
+{
+	PROP_0,
+	PROP_LINES_ADDED,
+	PROP_LINES_REMOVED,
+	PROP_MAX_LINES
+};
+
+struct _GitgStatViewPrivate
+{
+	gdouble color_added[3];
+	gdouble color_removed[3];
+
+	cairo_pattern_t *gradient_added;
+	cairo_pattern_t *gradient_removed;
+
+	guint lines_added;
+	guint lines_removed;
+	guint max_lines;
+
+	guint radius;
+	guint stat_padding;
+	gboolean show_lines;
+	guint lines_spacing;
+};
+
+G_DEFINE_TYPE (GitgStatView, gitg_stat_view, GTK_TYPE_DRAWING_AREA)
+
+static void
+clear_gradients (GitgStatView *view)
+{
+	if (view->priv->gradient_added)
+	{
+		cairo_pattern_destroy (view->priv->gradient_added);
+		view->priv->gradient_added = NULL;
+	}
+
+	if (view->priv->gradient_removed)
+	{
+		cairo_pattern_destroy (view->priv->gradient_removed);
+		view->priv->gradient_removed = NULL;
+	}
+}
+
+static void
+gitg_stat_view_finalize (GObject *object)
+{
+	GitgStatView *view = GITG_STAT_VIEW (object);
+
+	clear_gradients (view);
+
+	G_OBJECT_CLASS (gitg_stat_view_parent_class)->finalize (object);
+}
+
+static void
+update_colors (GitgStatView *view)
+{
+	GtkStyle *style;
+	GdkColor bg_color;
+	gdouble r, g, b;
+	gdouble hue, sat, val;
+
+	if (!gtk_widget_get_realized (GTK_WIDGET (view)))
+	{
+		return;
+	}
+
+	style = gtk_widget_get_style (GTK_WIDGET (view));
+	bg_color = style->base[gtk_widget_get_state (GTK_WIDGET (view))];
+
+	r = bg_color.red / 65535.0;
+	g = bg_color.green / 65535.0;
+	b = bg_color.blue / 65535.0;
+
+	gtk_rgb_to_hsv (r, g, b, &hue, &sat, &val);
+
+	sat = MIN(sat * 0.5 + 0.5, 1);
+	val = MIN((pow(val + 1, 3) - 1) / 7 * 0.6 + 0.2, 1);
+
+	gtk_hsv_to_rgb (0,
+	                sat,
+	                val,
+	                &(view->priv->color_removed[0]),
+	                &(view->priv->color_removed[1]),
+	                &(view->priv->color_removed[2]));
+
+	gtk_hsv_to_rgb (0.3,
+	                sat,
+	                val,
+	                &(view->priv->color_added[0]),
+	                &(view->priv->color_added[1]),
+	                &(view->priv->color_added[2]));
+
+	clear_gradients (view);
+}
+
+static void
+gitg_stat_view_realize (GtkWidget *widget)
+{
+	if (GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->realize)
+	{
+		GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->realize (widget);
+	}
+
+	update_colors (GITG_STAT_VIEW (widget));
+}
+
+static void
+update_styles (GitgStatView *view)
+{
+	gtk_style_get (gtk_widget_get_style (GTK_WIDGET (view)),
+	               GITG_TYPE_STAT_VIEW,
+	               "radius", &view->priv->radius,
+	               "stat-padding", &view->priv->stat_padding,
+	               "show-lines", &view->priv->show_lines,
+	               "lines-spacing", &view->priv->lines_spacing,
+	               NULL);
+}
+
+static void
+gitg_stat_view_style_set (GtkWidget *widget, GtkStyle *prev_style)
+{
+	if (GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->style_set)
+	{
+		GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->style_set (widget, prev_style);
+	}
+
+	update_colors (GITG_STAT_VIEW (widget));
+	update_styles (GITG_STAT_VIEW (widget));
+}
+
+static void
+multiply_color (gdouble *color, gdouble factor, gdouble *ret)
+{
+	guint i;
+
+	for (i = 0; i < 3; ++i)
+	{
+		ret[i] = color[i] * factor;
+	}
+}
+
+static cairo_pattern_t *
+create_gradient (gdouble *base_color,
+                 gint     y,
+                 gint     height)
+{
+	cairo_pattern_t *gradient;
+	gdouble ret[3];
+
+	gradient = cairo_pattern_create_linear (0, y, 0, height);
+
+	cairo_pattern_add_color_stop_rgb (gradient,
+	                                  0,
+	                                  base_color[0],
+	                                  base_color[1],
+	                                  base_color[2]);
+
+	multiply_color (base_color, 1.3, ret);
+
+	cairo_pattern_add_color_stop_rgb (gradient,
+	                                  1,
+	                                  ret[0],
+	                                  ret[1],
+	                                  ret[2]);
+
+	return gradient;
+}
+
+static void
+update_gradients (GitgStatView *view,
+                  GdkRectangle *alloc)
+{
+	if (view->priv->gradient_added == NULL)
+	{
+		view->priv->gradient_added = create_gradient (view->priv->color_added,
+		                                              0,
+		                                              alloc->height);
+	}
+
+	if (view->priv->gradient_removed == NULL)
+	{
+		view->priv->gradient_removed = create_gradient (view->priv->color_removed,
+		                                              0,
+		                                              alloc->height);
+	}
+}
+
+static void
+draw_stat (GitgStatView    *view,
+           cairo_t         *ctx,
+           gdouble         *color,
+           cairo_pattern_t *gradient,
+           gint             x,
+           gint             y,
+           gint             width,
+           gint             height)
+{
+	gdouble darker[3];
+	gdouble xoff;
+	cairo_matrix_t mat;
+
+	x += 0.5;
+	y += 0.5;
+	width -= 1;
+	height -= 1;
+
+	gitg_utils_rounded_rectangle (ctx,
+	                              x,
+	                              y,
+	                              width,
+	                              height,
+	                              view->priv->radius);
+
+	cairo_set_source (ctx, gradient);
+	cairo_fill_preserve (ctx);
+
+	multiply_color (color, 0.4, darker);
+
+	cairo_set_line_width (ctx, 1);
+
+	cairo_set_source_rgb (ctx, darker[0], darker[1], darker[2]);
+	cairo_stroke (ctx);
+
+	if (view->priv->show_lines)
+	{
+		xoff = x + view->priv->lines_spacing;
+
+		cairo_matrix_init_rotate (&mat, M_PI);
+		cairo_pattern_set_matrix (gradient, &mat);
+
+		cairo_set_source (ctx, gradient);
+
+		while (xoff < x + width - view->priv->lines_spacing / 2)
+		{
+			cairo_move_to (ctx, xoff, y + 2);
+			cairo_line_to (ctx, xoff, y + height - 2);
+			cairo_stroke (ctx);
+
+			xoff += view->priv->lines_spacing;
+		}
+
+		cairo_matrix_init_identity (&mat);
+		cairo_pattern_set_matrix (gradient, &mat);
+	}
+}
+
+static gboolean
+gitg_stat_view_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+	cairo_t *ctx;
+	GdkRectangle alloc;
+	guint added_width;
+	guint removed_width;
+	gdouble unit;
+	GitgStatView *view;
+	guint padding;
+
+	if (GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->expose_event)
+	{
+		GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->expose_event (widget, event);
+	}
+
+	view = GITG_STAT_VIEW (widget);
+
+	if (view->priv->max_lines == 0 ||
+	    (view->priv->lines_added == 0 && view->priv->lines_removed == 0))
+	{
+		return TRUE;
+	}
+
+	if (view->priv->lines_added == 0 || view->priv->lines_removed == 0)
+	{
+		padding = 0;
+	}
+	else
+	{
+		padding = 2;
+	}
+
+	ctx = gdk_cairo_create (event->window);
+
+	gdk_cairo_rectangle (ctx, &event->area);
+	cairo_clip (ctx);
+
+	gtk_widget_get_allocation (widget, &alloc);
+
+	update_gradients (view, &alloc);
+
+	unit = (alloc.width - padding) / (gdouble)view->priv->max_lines;
+
+	added_width = MAX(view->priv->radius * 2 + 1, (guint)(unit * view->priv->lines_added));
+	removed_width = MAX(view->priv->radius * 2 + 1, (guint)(unit * view->priv->lines_removed));
+
+	if (view->priv->lines_added > 0)
+	{
+		draw_stat (view,
+		           ctx,
+		           view->priv->color_added,
+		           view->priv->gradient_added,
+		           0,
+		           0,
+		           added_width,
+		           alloc.height);
+	}
+	else
+	{
+		added_width = 0;
+	}
+
+	if (view->priv->lines_removed > 0)
+	{
+		draw_stat (view,
+		           ctx,
+		           view->priv->color_removed,
+		           view->priv->gradient_removed,
+		           added_width + padding,
+		           0,
+		           removed_width,
+		           alloc.height);
+	}
+
+	cairo_destroy (ctx);
+
+	return TRUE;
+}
+
+static void
+gitg_stat_view_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+	GitgStatView *self = GITG_STAT_VIEW (object);
+	
+	switch (prop_id)
+	{
+		case PROP_LINES_ADDED:
+			self->priv->lines_added = g_value_get_uint (value);
+			gtk_widget_queue_draw (GTK_WIDGET (self));
+		break;
+		case PROP_LINES_REMOVED:
+			self->priv->lines_removed = g_value_get_uint (value);
+			gtk_widget_queue_draw (GTK_WIDGET (self));
+		break;
+		case PROP_MAX_LINES:
+			self->priv->max_lines = g_value_get_uint (value);
+			gtk_widget_queue_draw (GTK_WIDGET (self));
+		break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gitg_stat_view_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	GitgStatView *self = GITG_STAT_VIEW (object);
+	
+	switch (prop_id)
+	{
+		case PROP_LINES_ADDED:
+			g_value_set_uint (value, self->priv->lines_added);
+		break;
+		case PROP_LINES_REMOVED:
+			g_value_set_uint (value, self->priv->lines_removed);
+		break;
+		case PROP_MAX_LINES:
+			g_value_set_uint (value, self->priv->max_lines);
+		break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static gboolean
+gitg_stat_view_configure (GtkWidget *widget,
+                          GdkEventConfigure *event)
+{
+	gboolean ret;
+
+	if (GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->configure_event)
+	{
+		ret = GTK_WIDGET_CLASS (gitg_stat_view_parent_class)->configure_event (widget, event);
+	}
+	else
+	{
+		ret = FALSE;
+	}
+
+	clear_gradients (GITG_STAT_VIEW (widget));
+
+	return ret;
+}
+
+static void
+gitg_stat_view_class_init (GitgStatViewClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+	widget_class->expose_event = gitg_stat_view_expose;
+	widget_class->style_set = gitg_stat_view_style_set;
+	widget_class->realize = gitg_stat_view_realize;
+	widget_class->configure_event = gitg_stat_view_configure;
+
+	object_class->finalize = gitg_stat_view_finalize;
+	object_class->set_property = gitg_stat_view_set_property;
+	object_class->get_property = gitg_stat_view_get_property;
+
+	g_object_class_install_property (object_class,
+	                                 PROP_LINES_ADDED,
+	                                 g_param_spec_uint ("lines-added",
+	                                                    "Lines Added",
+	                                                    "Lines added",
+	                                                    0,
+	                                                    G_MAXUINT,
+	                                                    0,
+	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_LINES_REMOVED,
+	                                 g_param_spec_uint ("lines-removed",
+	                                                    "Lines Removed",
+	                                                    "Lines removed",
+	                                                    0,
+	                                                    G_MAXUINT,
+	                                                    0,
+	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_MAX_LINES,
+	                                 g_param_spec_uint ("max-lines",
+	                                                    "Max Lines",
+	                                                    "Max lines",
+	                                                    0,
+	                                                    G_MAXUINT,
+	                                                    0,
+	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	gtk_widget_class_install_style_property (widget_class,
+	                                         g_param_spec_uint ("radius",
+	                                                            "Radius",
+	                                                            "Radius",
+	                                                            0,
+	                                                            G_MAXUINT,
+	                                                            4,
+	                                                            G_PARAM_READWRITE));
+
+	gtk_widget_class_install_style_property (widget_class,
+	                                         g_param_spec_uint ("stat-padding",
+	                                                            "Stat padding",
+	                                                            "Stat padding",
+	                                                            0,
+	                                                            G_MAXUINT,
+	                                                            2,
+	                                                            G_PARAM_READWRITE));
+
+	gtk_widget_class_install_style_property (widget_class,
+	                                         g_param_spec_boolean ("show-lines",
+	                                                               "Show lines",
+	                                                               "Show lines",
+	                                                               TRUE,
+	                                                               G_PARAM_READWRITE));
+
+	gtk_widget_class_install_style_property (widget_class,
+	                                         g_param_spec_uint ("lines-spacing",
+	                                                            "Lines spacing",
+	                                                            "Lines spacing",
+	                                                            1,
+	                                                            G_MAXUINT,
+	                                                            10,
+	                                                            G_PARAM_READWRITE));
+
+	g_type_class_add_private (object_class, sizeof(GitgStatViewPrivate));
+}
+
+static void
+gitg_stat_view_init (GitgStatView *self)
+{
+	self->priv = GITG_STAT_VIEW_GET_PRIVATE (self);
+}
+
+GtkWidget *
+gitg_stat_view_new (guint lines_added,
+                    guint lines_removed,
+                    guint max_lines)
+{
+	return g_object_new (GITG_TYPE_STAT_VIEW,
+	                     "lines-added",
+	                     lines_added,
+	                     "lines-removed",
+	                     lines_removed,
+	                     "max-lines",
+	                     max_lines,
+	                     NULL);
+}
diff --git a/gitg/gitg-stat-view.h b/gitg/gitg-stat-view.h
new file mode 100644
index 0000000..e78a636
--- /dev/null
+++ b/gitg/gitg-stat-view.h
@@ -0,0 +1,37 @@
+#ifndef __GITG_STAT_VIEW_H__
+#define __GITG_STAT_VIEW_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GITG_TYPE_STAT_VIEW		(gitg_stat_view_get_type ())
+#define GITG_STAT_VIEW(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GITG_TYPE_STAT_VIEW, GitgStatView))
+#define GITG_STAT_VIEW_CONST(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), GITG_TYPE_STAT_VIEW, GitgStatView const))
+#define GITG_STAT_VIEW_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GITG_TYPE_STAT_VIEW, GitgStatViewClass))
+#define GITG_IS_STAT_VIEW(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GITG_TYPE_STAT_VIEW))
+#define GITG_IS_STAT_VIEW_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GITG_TYPE_STAT_VIEW))
+#define GITG_STAT_VIEW_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GITG_TYPE_STAT_VIEW, GitgStatViewClass))
+
+typedef struct _GitgStatView		GitgStatView;
+typedef struct _GitgStatViewClass	GitgStatViewClass;
+typedef struct _GitgStatViewPrivate	GitgStatViewPrivate;
+
+struct _GitgStatView {
+	GtkDrawingArea parent;
+
+	GitgStatViewPrivate *priv;
+};
+
+struct _GitgStatViewClass {
+	GtkDrawingAreaClass parent_class;
+};
+
+GType gitg_stat_view_get_type (void) G_GNUC_CONST;
+GtkWidget *gitg_stat_view_new (guint lines_added,
+                               guint lines_removed,
+                               guint max_lines);
+
+G_END_DECLS
+
+#endif /* __GITG_STAT_VIEW_H__ */
diff --git a/gitg/gitg-window.c b/gitg/gitg-window.c
index dc035bb..855d94a 100644
--- a/gitg/gitg-window.c
+++ b/gitg/gitg-window.c
@@ -1252,7 +1252,21 @@ parse_gitg_uri (GFile  *file,
 	/* Extract path and sha information */
 	gchar *uri = g_file_get_uri (file);
 	gchar *fd = strrchr (uri, ':');
-	gint pos = fd ? fd - uri : 0;
+	gchar *sel = NULL;
+	gint pos = 0;
+
+	if (fd)
+	{
+		sel = strchr (fd, '/');
+
+		if (sel)
+		{
+			*sel = '\0';
+			sel += 1;
+		}
+
+		pos = fd - uri;
+	}
 
 	if (pos > 5 && strlen (uri) - pos - 1 <= 40)
 	{
diff --git a/gitg/gitg.c b/gitg/gitg.c
index 555060e..7070595 100644
--- a/gitg/gitg.c
+++ b/gitg/gitg.c
@@ -37,6 +37,7 @@
 
 static gboolean commit_mode = FALSE;
 static gchar *select_sha1 = NULL;
+static GtkLinkButtonUriFunc original_link_button_hook;
 
 static void
 show_version_and_quit (void)
@@ -166,6 +167,26 @@ set_icons ()
 	g_list_free (icons);
 }
 
+static void
+link_button_uri_hook (GtkLinkButton *button,
+                      gchar const   *link_,
+                      GitgWindow    *window)
+{
+	GFile *file;
+
+	file = g_file_new_for_uri (link_);
+
+	if (!g_file_has_uri_scheme (file, "gitg"))
+	{
+		original_link_button_hook (button, link_, NULL);
+	}
+	else
+	{
+	}
+
+	g_object_unref (file);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -205,6 +226,10 @@ main (int argc, char **argv)
 		gitg_window_show_commit (window);
 	}
 
+	original_link_button_hook = gtk_link_button_set_uri_hook ((GtkLinkButtonUriFunc)link_button_uri_hook,
+	                                                          window,
+	                                                          NULL);
+
 	gtk_main ();
 
 	/* Finalize settings */



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