[gitg] Added DND support for revisions (treeish, text and uri)
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gitg] Added DND support for revisions (treeish, text and uri)
- Date: Sat, 9 Jan 2010 19:29:21 +0000 (UTC)
commit 2701a3b1c76b28c764ebf20d8c81af4a646232cc
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Sat Jan 9 20:18:37 2010 +0100
Added DND support for revisions (treeish, text and uri)
gitg/gitg-dnd.c | 392 ++++++++++++++++++++++++++++++++++++++++----
gitg/gitg-label-renderer.c | 4 +-
2 files changed, 363 insertions(+), 33 deletions(-)
---
diff --git a/gitg/gitg-dnd.c b/gitg/gitg-dnd.c
index b76b769..72152a1 100644
--- a/gitg/gitg-dnd.c
+++ b/gitg/gitg-dnd.c
@@ -2,16 +2,25 @@
#include "gitg-ref.h"
#include "gitg-cell-renderer-path.h"
#include "gitg-utils.h"
+#include <string.h>
enum
{
- DRAG_TARGET_REF = 1
+ DRAG_TARGET_REF = 1,
+ DRAG_TARGET_TREEISH,
+ DRAG_TARGET_REVISION,
+ DRAG_TARGET_TEXT,
+ DRAG_TARGET_URI
};
-static GtkTargetEntry target_entries[] = {
+static GtkTargetEntry target_dest_entries[] = {
{"gitg-ref", GTK_TARGET_SAME_WIDGET, DRAG_TARGET_REF}
};
+static GtkTargetEntry target_source_entries[] = {
+ {"x-gitg/treeish-list", GTK_TARGET_OTHER_APP, DRAG_TARGET_TREEISH}
+};
+
typedef struct
{
GtkTreeView *tree_view;
@@ -27,6 +36,8 @@ typedef struct
gboolean is_drag;
GtkTargetList *target_list;
+ GtkTargetList *revision_target_list;
+ GitgRevision *revision;
guint scroll_timeout;
} GitgDndData;
@@ -51,8 +62,14 @@ gitg_dnd_data_new ()
{
GitgDndData *data = g_slice_new0 (GitgDndData);
- data->target_list = gtk_target_list_new (target_entries,
- G_N_ELEMENTS (target_entries));
+ data->target_list = gtk_target_list_new (target_dest_entries,
+ G_N_ELEMENTS (target_dest_entries));
+
+ data->revision_target_list = gtk_target_list_new (target_source_entries,
+ G_N_ELEMENTS (target_source_entries));
+
+ gtk_target_list_add_text_targets (data->revision_target_list, DRAG_TARGET_TEXT);
+ gtk_target_list_add_uri_targets (data->revision_target_list, DRAG_TARGET_URI);
return data;
}
@@ -61,13 +78,21 @@ static void
gitg_dnd_data_free (GitgDndData *data)
{
gtk_target_list_unref (data->target_list);
+ gtk_target_list_unref (data->revision_target_list);
+
remove_scroll_timeout (data);
g_slice_free (GitgDndData, data);
}
static GitgRef *
-get_ref_at_pos (GtkTreeView *tree_view, gint x, gint y, gint *hot_x, gint *hot_y, GitgCellRendererPath **renderer, GtkTreePath **tp)
+get_ref_at_pos (GtkTreeView *tree_view,
+ gint x,
+ gint y,
+ gint *hot_x,
+ gint *hot_y,
+ GitgCellRendererPath **renderer,
+ GtkTreePath **tp)
{
gint cell_x;
gint cell_y;
@@ -119,6 +144,52 @@ get_ref_at_pos (GtkTreeView *tree_view, gint x, gint y, gint *hot_x, gint *hot_y
return ref;
}
+static GitgRevision *
+get_revision_at_pos (GtkTreeView *tree_view,
+ gint x,
+ gint y,
+ GtkTreePath **tp)
+{
+ gint cell_x;
+ gint cell_y;
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GitgRevision *revision;
+
+ if (!gtk_tree_view_get_path_at_pos (tree_view,
+ x,
+ y,
+ &path,
+ &column,
+ &cell_x,
+ &cell_y))
+ {
+ return NULL;
+ }
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ {
+ return NULL;
+ }
+
+ gtk_tree_model_get (model, &iter, 0, &revision, -1);
+
+ if (revision && tp)
+ {
+ *tp = path;
+ }
+ else
+ {
+ gtk_tree_path_free (path);
+ }
+
+ return revision;
+}
+
static gboolean
can_drag (GitgRef *ref)
{
@@ -164,6 +235,92 @@ can_drop (GitgRef *source, GitgRef *dest)
return FALSE;
}
+/* Copied from gitg-label-renderer
+ * TODO: refactor
+ */
+static inline guint8
+convert_color_channel (guint8 src,
+ guint8 alpha)
+{
+ return alpha ? src / (alpha / 255.0) : 0;
+}
+
+static void
+convert_bgra_to_rgba (guint8 const *src,
+ guint8 *dst,
+ gint width,
+ gint height)
+{
+ guint8 const *src_pixel = src;
+ guint8 * dst_pixel = dst;
+ int y;
+
+ for (y = 0; y < height; y++)
+ {
+ int x;
+
+ for (x = 0; x < width; x++)
+ {
+ dst_pixel[0] = convert_color_channel (src_pixel[2],
+ src_pixel[3]);
+ dst_pixel[1] = convert_color_channel (src_pixel[1],
+ src_pixel[3]);
+ dst_pixel[2] = convert_color_channel (src_pixel[0],
+ src_pixel[3]);
+ dst_pixel[3] = src_pixel[3];
+
+ dst_pixel += 4;
+ src_pixel += 4;
+ }
+ }
+}
+
+static GdkPixbuf *
+create_revision_drag_icon (GtkTreeView *tree_view,
+ GitgRevision *revision)
+{
+ gchar const *subject = gitg_revision_get_subject (revision);
+ gchar *sha1 = gitg_revision_get_sha1 (revision);
+
+ /* Only take first 8 characters */
+ sha1[8] = '\0';
+
+ gchar *text = g_strdup_printf ("%s: %s", sha1, subject);
+
+ PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (tree_view), text);
+ gint width;
+ gint height;
+
+ pango_layout_get_pixel_size (layout, &width, &height);
+
+ cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width + 4, height + 4);
+ cairo_t *context = cairo_create (surface);
+
+ cairo_rectangle (context, 0, 0, width + 4, height + 4);
+ cairo_set_source_rgb (context, 1, 1, 1);
+ cairo_fill (context);
+
+ cairo_translate (context, 2, 2);
+ cairo_set_source_rgb (context, 0, 0, 0);
+ pango_cairo_show_layout (context, layout);
+
+ guint8 *data = cairo_image_surface_get_data (surface);
+ GdkPixbuf *ret = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width + 4, height + 4);
+ guint8 *pixdata = gdk_pixbuf_get_pixels (ret);
+
+ convert_bgra_to_rgba (data, pixdata, width + 4, height + 4);
+
+ cairo_destroy (context);
+ cairo_surface_destroy (surface);
+
+ g_object_unref (layout);
+
+ g_free (text);
+ g_free (sha1);
+
+ return ret;
+}
+
static void
begin_drag (GtkWidget *widget,
GdkEvent *event,
@@ -182,35 +339,72 @@ begin_drag (GtkWidget *widget,
&cell,
NULL);
- if (!ref || !can_drag (ref))
+ if (ref && !can_drag (ref))
{
return;
}
+ else if (ref)
+ {
+ /* This is a DND operation on a ref */
+ data->ref = ref;
+ gitg_ref_set_state (ref, GITG_REF_STATE_NONE);
+
+ GdkDragContext *context = gtk_drag_begin (widget,
+ data->target_list,
+ GDK_ACTION_MOVE,
+ 1,
+ event);
+
+ guint minwidth;
+ guint h;
+ gdk_display_get_maximal_cursor_size (gtk_widget_get_display (widget), &minwidth, &h);
+
+ GdkPixbuf *pixbuf = gitg_cell_renderer_path_render_ref (GTK_WIDGET (tree_view),
+ cell,
+ ref,
+ minwidth + 1);
+
+ if (pixbuf)
+ {
+ gtk_drag_set_icon_pixbuf (context, pixbuf, hot_x, hot_y);
+ g_object_unref (pixbuf);
+ }
+ }
+ else
+ {
+ /* This is a normal DND operation which is just possibly just about
+ a SHA */
+ GitgRevision *revision;
+ GtkTreePath *path;
- data->ref = ref;
- gitg_ref_set_state (ref, GITG_REF_STATE_NONE);
+ revision = get_revision_at_pos (tree_view,
+ (gint)data->x,
+ (gint)data->y,
+ &path);
- GdkDragContext *context = gtk_drag_begin (widget,
- data->target_list,
- GDK_ACTION_MOVE,
- 1,
- event);
+ if (revision)
+ {
+ /* Make a DND for the revision */
+ data->revision = revision;
- guint minwidth;
- guint h;
- gdk_display_get_maximal_cursor_size (gtk_widget_get_display (widget), &minwidth, &h);
+ GdkDragContext *context = gtk_drag_begin (widget,
+ data->revision_target_list,
+ GDK_ACTION_COPY,
+ 1,
+ event);
+ GdkPixbuf *icon;
- GdkPixbuf *pixbuf = gitg_cell_renderer_path_render_ref (GTK_WIDGET (tree_view),
- cell,
- ref,
- minwidth + 1);
+ icon = create_revision_drag_icon (tree_view, revision);
- if (pixbuf)
- {
- gtk_drag_set_icon_pixbuf (context, pixbuf, hot_x, hot_y);
- g_object_unref (pixbuf);
- }
+ if (icon)
+ {
+ gtk_drag_set_icon_pixbuf (context, icon, 0, 0);
+ g_object_unref (icon);
+ }
+ gtk_tree_path_free (path);
+ }
+ }
}
static void
@@ -288,11 +482,11 @@ static void
add_scroll_timeout (GitgDndData *data)
{
if (data->scroll_timeout == 0)
- {
+ {
data->scroll_timeout = g_timeout_add (50,
(GSourceFunc)vertical_autoscroll,
data);
- }
+ }
}
@@ -459,7 +653,137 @@ gitg_drag_source_leave_cb (GtkWidget *widget,
return FALSE;
}
-void
+static void
+remove_trailing_newlines (gchar **lines)
+{
+ gint lastnewline = -1;
+ gchar **ptr = lines;
+ gint i = 0;
+
+ while (ptr && *ptr)
+ {
+ if (lastnewline == -1 && **ptr == '\0')
+ {
+ lastnewline = i;
+ }
+ else if (lastnewline != -1 && **ptr != '\0')
+ {
+ lastnewline = -1;
+ }
+
+ ++i;
+ ++ptr;
+ }
+
+ if (lastnewline == -1)
+ {
+ return;
+ }
+
+ while (lines[lastnewline])
+ {
+ g_free (lines[lastnewline]);
+ lines[lastnewline] = NULL;
+
+ ++lastnewline;
+ }
+}
+
+static gchar *
+revision_to_text (GitgRepository *repository,
+ GitgRevision *revision)
+{
+ gchar **lines;
+ gchar *sha1 = gitg_revision_get_sha1 (revision);
+
+ lines = gitg_repository_command_with_outputv (repository,
+ NULL,
+ "log",
+ "-1",
+ "--pretty=format:%h: %s%n%n%b",
+ sha1,
+ NULL);
+
+ remove_trailing_newlines (lines);
+ gchar *ret = g_strjoinv ("\n", lines);
+
+ g_strfreev (lines);
+ g_free (sha1);
+
+ return ret;
+}
+
+static gchar *
+revision_to_uri (GitgRepository *repository,
+ GitgRevision *revision)
+{
+ gchar const *path = gitg_repository_get_path (repository);
+ gchar *sha1 = gitg_revision_get_sha1 (revision);
+
+ gchar *ret = g_strdup_printf ("gitg://%s:%s", path, sha1);
+ g_free (sha1);
+
+ return ret;
+}
+
+static gchar *
+revision_to_treeish (GitgRepository *repository,
+ GitgRevision *revision)
+{
+ gchar const *path = gitg_repository_get_path (repository);
+ gchar const *subject = gitg_revision_get_subject (revision);
+ gchar *sha1 = gitg_revision_get_sha1 (revision);
+
+ gchar *ret = g_strdup_printf ("%s\n%s: %s", path, sha1, subject);
+ g_free (sha1);
+
+ return ret;
+}
+
+static void
+gitg_drag_source_data_get_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection,
+ guint info,
+ guint time,
+ GitgDndData *data)
+{
+ if (!data->revision)
+ {
+ return;
+ }
+
+ GitgRepository *repository = GITG_REPOSITORY (gtk_tree_view_get_model (GTK_TREE_VIEW (widget)));
+
+ switch (info)
+ {
+ case DRAG_TARGET_TEXT:
+ {
+ gchar *text = revision_to_text (repository, data->revision);
+ gtk_selection_data_set_text (selection, text, -1);
+ g_free (text);
+ }
+ break;
+ case DRAG_TARGET_TREEISH:
+ {
+ gchar *treeish = revision_to_treeish (repository, data->revision);
+
+ gtk_selection_data_set (selection, selection->target, 8, treeish, strlen (treeish));
+ }
+ break;
+ case DRAG_TARGET_URI:
+ {
+ gchar *uri = revision_to_uri (repository, data->revision);
+ gchar *uris[] = {uri, NULL};
+
+ gtk_selection_data_set_uris (selection, uris);
+ g_free (uri);
+ }
+ break;
+ }
+}
+
+void
gitg_dnd_enable (GtkTreeView *tree_view, GitgDndCallback callback, gpointer callback_data)
{
if (GITG_DND_GET_DATA (tree_view))
@@ -486,11 +810,16 @@ gitg_dnd_enable (GtkTreeView *tree_view, GitgDndCallback callback, gpointer call
gtk_drag_dest_set (GTK_WIDGET (tree_view),
0,
- target_entries,
- G_N_ELEMENTS (target_entries),
+ target_dest_entries,
+ G_N_ELEMENTS (target_dest_entries),
GDK_ACTION_MOVE);
- g_signal_connect (tree_view,
+ g_signal_connect (tree_view,
+ "drag-data-get",
+ G_CALLBACK (gitg_drag_source_data_get_cb),
+ data);
+
+ g_signal_connect (tree_view,
"button-press-event",
G_CALLBACK (gitg_drag_source_event_cb),
data);
@@ -532,6 +861,7 @@ gitg_dnd_disable (GtkTreeView *tree_view)
g_signal_handlers_disconnect_by_func (tree_view, gitg_drag_source_motion_cb, data);
g_signal_handlers_disconnect_by_func (tree_view, gitg_drag_source_drop_cb, data);
g_signal_handlers_disconnect_by_func (tree_view, gitg_drag_source_leave_cb, data);
+ g_signal_handlers_disconnect_by_func (tree_view, gitg_drag_source_data_get_cb, data);
g_object_set_data (G_OBJECT (tree_view), GITG_DND_DATA_KEY, NULL);
}
diff --git a/gitg/gitg-label-renderer.c b/gitg/gitg-label-renderer.c
index 3d96fe5..1b20495 100644
--- a/gitg/gitg-label-renderer.c
+++ b/gitg/gitg-label-renderer.c
@@ -241,14 +241,14 @@ gitg_label_renderer_get_ref_at_pos (GtkWidget *widget, PangoFontDescription *fon
return ret;
}
-inline guint8
+static inline guint8
convert_color_channel (guint8 src,
guint8 alpha)
{
return alpha ? src / (alpha / 255.0) : 0;
}
-void
+static void
convert_bgra_to_rgba (guint8 const *src,
guint8 *dst,
gint width,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]