[gitg] Added async progress for branch actions



commit 1f1c4358e8ec05d13e8095b04bfd51137ab270f0
Author: Jesse van den Kieboom <jessevdk gnome org>
Date:   Sat Jul 4 19:01:53 2009 +0200

    Added async progress for branch actions

 gitg/gitg-branch-actions.c |  246 ++++++++++++++++++++++++++++++++++++++-----
 gitg/gitg-branch-actions.h |    4 +-
 gitg/gitg-repository.c     |    2 +-
 gitg/gitg-window.c         |   39 +++++++-
 4 files changed, 259 insertions(+), 32 deletions(-)
---
diff --git a/gitg/gitg-branch-actions.c b/gitg/gitg-branch-actions.c
index d2b6f50..10e77ed 100644
--- a/gitg/gitg-branch-actions.c
+++ b/gitg/gitg-branch-actions.c
@@ -2,6 +2,166 @@
 
 #include "gitg-branch-actions.h"
 
+typedef enum
+{
+	GITG_PROGRESS_SUCCESS,
+	GITG_PROGRESS_ERROR,
+	GITG_PROGRESS_CANCELLED
+} GitgProgress;
+
+typedef void (*ProgressCallback)(GitgWindow *window, GitgProgress progress, gpointer data);
+
+typedef struct
+{
+	GitgWindow *window;
+	GitgRunner *runner;
+	
+	ProgressCallback callback;
+	gpointer callback_data;
+	
+	guint timeout_id;
+	
+	GtkDialog *dialog;
+	GtkProgressBar *progress;
+} ProgressInfo;
+
+static void
+free_progress_info (ProgressInfo *info)
+{
+	if (info->timeout_id)
+	{
+		g_source_remove (info->timeout_id);
+	}
+
+	g_object_unref (info->runner);
+	g_slice_free (ProgressInfo, info);
+}
+
+static gchar const **
+parse_valist(va_list ap)
+{
+	gchar const *a;
+	gchar const **ret = NULL;
+	guint num = 0;
+	
+	while ((a = va_arg(ap, gchar const *)) != NULL)
+	{
+		ret = g_realloc(ret, sizeof(gchar const *) * (++num + 1));
+		ret[num - 1] = a;
+	}
+	
+	ret[num] = NULL;
+	return ret;
+}
+
+static void
+on_progress_end (GitgRunner *runner, gboolean cancelled, ProgressInfo *info)
+{
+	GitgProgress progress;
+	
+	if (cancelled)
+	{
+		progress = GITG_PROGRESS_CANCELLED;
+	}
+	else if (gitg_runner_get_exit_status (runner) != 0)
+	{
+		progress = GITG_PROGRESS_ERROR;
+	}
+	else
+	{
+		progress = GITG_PROGRESS_SUCCESS;
+	}
+
+	info->callback (info->window, progress, info->callback_data);
+	free_progress_info (info);
+}
+
+static void
+on_progress_response (GtkDialog *dialog, GtkResponseType response, ProgressInfo *info)
+{
+	gitg_runner_cancel (info->runner);
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static gboolean
+on_progress_timeout (ProgressInfo *info)
+{
+	gtk_progress_bar_pulse (info->progress);
+	return TRUE;
+}
+
+static GitgRunner *
+run_progress (GitgWindow       *window,
+              gchar const      *title,
+              gchar const      *message,
+              ProgressCallback  callback,
+              gpointer          callback_data,
+              ...)
+{
+	va_list ap;
+	
+	// Create runner
+	va_start (ap, callback_data);
+	
+	GitgRunner *runner = gitg_runner_new (1000);
+	gchar const **argv = parse_valist (ap);
+	
+	if (!gitg_repository_run_command (gitg_window_get_repository (window),
+	                                  runner,
+	                                  argv,
+	                                  NULL))
+	{
+		g_free (argv);
+		g_object_unref (runner);
+		
+		callback (window, GITG_PROGRESS_ERROR, callback_data);	
+
+		return NULL;
+	}
+	
+	g_free (argv);
+	
+	// Create dialog to show progress	
+	GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
+	GtkWidget *dlg;
+
+	dlg = gtk_message_dialog_new (GTK_WINDOW (window),
+	                              flags,
+	                              GTK_MESSAGE_INFO,
+	                              GTK_BUTTONS_CANCEL,
+	                              "%s",
+	                              title);
+
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg),
+	                                          "%s",
+	                                          message);
+
+	// Add progress bar
+	GtkWidget *area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
+	GtkWidget *progress = gtk_progress_bar_new ();
+	gtk_widget_show (progress);
+	
+	gtk_box_pack_start (GTK_BOX (area), progress, FALSE, FALSE, 0);
+	
+	gtk_widget_show (dlg);
+	
+	ProgressInfo *info = g_slice_new0 (ProgressInfo);
+	
+	info->dialog = GTK_DIALOG (dlg);
+	info->progress = GTK_PROGRESS_BAR (progress);
+	info->callback = callback;
+	info->callback_data = callback_data;
+	info->window = window;
+	info->runner = g_object_ref (runner);
+	
+	info->timeout_id = g_timeout_add (100, (GSourceFunc)on_progress_timeout, info);
+	
+	g_signal_connect (dlg, "response", G_CALLBACK (on_progress_response), info);
+	g_signal_connect (runner, "end-loading", G_CALLBACK (on_progress_end), info);
+	
+	return runner;
+}
+              
 static gint
 message_dialog (GitgWindow     *window,
                 GtkMessageType  type,
@@ -38,6 +198,7 @@ message_dialog (GitgWindow     *window,
 	
 	button = gtk_button_new_from_stock (accept ? GTK_STOCK_CANCEL : GTK_STOCK_OK);
 	gtk_widget_show (button);
+	
 	gtk_dialog_add_action_widget (GTK_DIALOG (dlg), 
 	                              button, 
 	                              accept ? GTK_RESPONSE_CANCEL : GTK_RESPONSE_ACCEPT);
@@ -336,18 +497,48 @@ gitg_branch_actions_rebase (GitgWindow *window,
 	return FALSE;
 }
 
+typedef struct
+{
+	GitgRef *source;
+	GitgRef *dest;
+} PushInfo;
 
-gboolean
+static void
+on_push_result (GitgWindow   *window,
+                GitgProgress  progress,
+                gpointer      data)
+{
+	PushInfo *info = (PushInfo *)data;
+
+	if (progress == GITG_PROGRESS_ERROR)
+	{
+		message_dialog (window,
+			            GTK_MESSAGE_ERROR,
+			            _("Failed to push local branch <%s> to remote <%s>"),
+			            NULL,
+			            NULL,
+			            gitg_ref_get_shortname (info->source),
+			            gitg_ref_get_shortname (info->dest));
+	}
+	else if (progress == GITG_PROGRESS_SUCCESS)
+	{
+		gitg_repository_reload (gitg_window_get_repository (window));
+	}
+
+	gitg_ref_free (info->source);
+	gitg_ref_free (info->dest);
+	g_slice_free (PushInfo, info);
+}
+
+GitgRunner *
 gitg_branch_actions_push (GitgWindow *window,
                           GitgRef    *source,
                           GitgRef    *dest)
 {
-	g_return_val_if_fail (GITG_IS_WINDOW (window), FALSE);
-	g_return_val_if_fail (gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH, FALSE);
-	g_return_val_if_fail (gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_REMOTE, FALSE);
+	g_return_val_if_fail (GITG_IS_WINDOW (window), NULL);
+	g_return_val_if_fail (gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH, NULL);
+	g_return_val_if_fail (gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_REMOTE, NULL);
 	
-	GitgRepository *repository = gitg_window_get_repository (window);
-
 	if (message_dialog (window,
 	                    GTK_MESSAGE_QUESTION,
 	                    _("Are you sure you want to push <%s> to <%s>?"),
@@ -356,39 +547,36 @@ gitg_branch_actions_push (GitgWindow *window,
 	                    gitg_ref_get_shortname (source),
 	                    gitg_ref_get_shortname (dest)) != GTK_RESPONSE_ACCEPT)
 	{
-		return FALSE;
+		return NULL;
 	}
 
 	gchar const *prefix = gitg_ref_get_prefix (dest);
 	gchar *local = gitg_ref_get_local_name (dest);
 	gchar const *name = gitg_ref_get_shortname (source);
-	gboolean ret = FALSE;
 	
 	gchar *spec = g_strconcat (name, ":", local, NULL);
+	gchar *message = g_strdup_printf (_("Pushing local branch `%s' to remote branch `%s'"),
+	                                  gitg_ref_get_shortname (source),
+	                                  gitg_ref_get_shortname (dest));
 	
-	if (!gitg_repository_commandv (repository,
-	                               NULL,
-	                               "push",
-	                               prefix,
-	                               spec,
-	                               NULL))
-	{
-		message_dialog (window,
-		                GTK_MESSAGE_ERROR,
-		                _("Failed to push local branch <%s> to remote <%s>"),
-		                NULL,
-		                NULL,
-		                name,
-		                gitg_ref_get_shortname (dest));
-	}
-	else
-	{
-		gitg_repository_reload (repository);
-		ret = TRUE;
-	}
+	GitgRunner *ret;
+	PushInfo *info = g_slice_new (PushInfo);
+	info->source = gitg_ref_copy (source);
+	info->dest = gitg_ref_copy (dest);
 	
-	g_free (spec);
+	ret = run_progress (window, 
+	                    _("Push"), 
+	                    message, 
+	                    on_push_result,  
+	                    info,
+	                    "push",
+	                    prefix,
+	                    spec,
+	                    NULL);
+	
+	g_free (message);
 	g_free (local);
+	g_free (spec);
 	
 	return ret;
 }
diff --git a/gitg/gitg-branch-actions.h b/gitg/gitg-branch-actions.h
index 9e0d6ea..cf47d87 100644
--- a/gitg/gitg-branch-actions.h
+++ b/gitg/gitg-branch-actions.h
@@ -32,7 +32,9 @@ gboolean gitg_branch_actions_remove (GitgWindow *window, GitgRef *ref);
 gboolean gitg_branch_actions_checkout (GitgWindow *window, GitgRef *ref);
 gboolean gitg_branch_actions_merge (GitgWindow *window, GitgRef *source, GitgRef *dest);
 gboolean gitg_branch_actions_rebase (GitgWindow *window, GitgRef *source, GitgRef *dest);
-gboolean gitg_branch_actions_push (GitgWindow *window, GitgRef *source, GitgRef *dest);
+
+GitgRunner *gitg_branch_actions_push (GitgWindow *window, GitgRef *source, GitgRef *dest);
+
 G_END_DECLS
 
 #endif /* __GITG_BRANCH_ACTIONS_H__ */
diff --git a/gitg/gitg-repository.c b/gitg/gitg-repository.c
index 90f9d6d..e3aeac5 100644
--- a/gitg/gitg-repository.c
+++ b/gitg/gitg-repository.c
@@ -1276,7 +1276,7 @@ gitg_repository_command_with_output(GitgRepository *repository, gchar const **ar
 	return gitg_repository_command_with_input_and_output(repository, argv, NULL, error);
 }
 
-gchar const **
+static gchar const **
 parse_valist(va_list ap)
 {
 	gchar const *a;
diff --git a/gitg/gitg-window.c b/gitg/gitg-window.c
index e38f86f..0c1b65d 100644
--- a/gitg/gitg-window.c
+++ b/gitg/gitg-window.c
@@ -86,6 +86,8 @@ struct _GitgWindowPrivate
 	guint merge_rebase_uid;
 	GtkActionGroup *merge_rebase_action_group;
 	GitgRef *popup_ref;
+	
+	GList *branch_actions;
 };
 
 static gboolean on_tree_view_motion(GtkTreeView *treeview, GdkEventMotion *event, GitgWindow *window);
@@ -100,6 +102,31 @@ static GtkBuildableIface parent_iface;
 static GtkWindowClass *parent_class = NULL;
 
 static void
+on_branch_action_runner_end (GitgRunner *runner, gboolean cancelled, GitgWindow *window)
+{
+	window->priv->branch_actions = g_list_remove (window->priv->branch_actions, runner);
+	g_object_unref (runner);
+}
+
+static gboolean
+add_branch_action (GitgWindow *window, GitgRunner *runner)
+{
+	if (runner != NULL && gitg_runner_running (runner))
+	{
+		window->priv->branch_actions = g_list_prepend (window->priv->branch_actions, runner);
+		
+		g_signal_connect (runner, "end-loading", G_CALLBACK (on_branch_action_runner_end), window);
+	}
+	else if (runner)
+	{
+		g_object_unref (runner);
+		runner = NULL;
+	}
+	
+	return runner != NULL;
+}
+
+static void
 gitg_window_finalize(GObject *object)
 {
 	GitgWindow *self = GITG_WINDOW(object);
@@ -107,6 +134,16 @@ gitg_window_finalize(GObject *object)
 	g_timer_destroy(self->priv->load_timer);
 	gdk_cursor_unref(self->priv->hand);
 	
+	GList *copy = g_list_copy (self->priv->branch_actions);
+	GList *item;
+	
+	for (item = copy; item; item = g_list_next (item))
+	{
+		gitg_runner_cancel (GITG_RUNNER (item->data));
+	}
+	
+	g_list_free (copy);
+
 	G_OBJECT_CLASS(gitg_window_parent_class)->finalize(object);
 }
 
@@ -471,7 +508,7 @@ on_refs_dnd (GitgRef *source, GitgRef *dest, gboolean dropped, GitgWindow *windo
 	if (gitg_ref_get_ref_type (source) == GITG_REF_TYPE_BRANCH &&
 	    gitg_ref_get_ref_type (dest) == GITG_REF_TYPE_REMOTE)
 	{
-		ret = gitg_branch_actions_push (window, source, dest);
+		ret = add_branch_action (window, gitg_branch_actions_push (window, source, dest));
 	}
 
 	gtk_statusbar_push (window->priv->statusbar, 0, "");



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