[gnome-builder/wip/chergert/perspective] git: more git clone porting to perspectives



commit 84d19fed4ef7925fb1929ebbeef8322a9ead8284
Author: Christian Hergert <chergert redhat com>
Date:   Mon Nov 30 21:41:15 2015 -0800

    git: more git clone porting to perspectives

 data/ui/ide-genesis-perspective.ui       |    5 +-
 data/ui/ide-greeter-perspective.ui       |    6 +-
 libide/genesis/ide-genesis-perspective.c |   69 ++++++-
 plugins/git/ide-git-clone-widget.c       |  302 +++++++++++++++++++++++++++---
 plugins/git/ide-git-clone-widget.ui      |   33 +++-
 plugins/git/ide-git-genesis-addin.c      |    2 +-
 6 files changed, 372 insertions(+), 45 deletions(-)
---
diff --git a/data/ui/ide-genesis-perspective.ui b/data/ui/ide-genesis-perspective.ui
index b1eb27b..fd65884 100644
--- a/data/ui/ide-genesis-perspective.ui
+++ b/data/ui/ide-genesis-perspective.ui
@@ -9,7 +9,7 @@
         <property name="expand">true</property>
         <property name="visible">true</property>
         <child>
-          <object class="EggBox">
+          <object class="EggBox" id="main_page">
             <property name="halign">center</property>
             <property name="width-request">550</property>
             <property name="max-width-request">550</property>
@@ -37,8 +37,7 @@
     <property name="visible">true</property>
     <child>
       <object class="GtkButton">
-        <property name="action-name">win.perspective</property>
-        <property name="action-target">'greeter'</property>
+        <property name="action-name">perspective.go-previous</property>
         <property name="visible">true</property>
         <style>
           <class name="image-button"/>
diff --git a/data/ui/ide-greeter-perspective.ui b/data/ui/ide-greeter-perspective.ui
index 28e1a1a..5397124 100644
--- a/data/ui/ide-greeter-perspective.ui
+++ b/data/ui/ide-greeter-perspective.ui
@@ -175,11 +175,11 @@
                       </object>
                     </child>
                   </object>
-                  <packing>
-                    <property name="name">assistants</property>
-                  </packing>
                 </child>
               </object>
+              <packing>
+                <property name="name">assistants</property>
+              </packing>
             </child>
           </object>
         </child>
diff --git a/libide/genesis/ide-genesis-perspective.c b/libide/genesis/ide-genesis-perspective.c
index 20f6b46..55cd264 100644
--- a/libide/genesis/ide-genesis-perspective.c
+++ b/libide/genesis/ide-genesis-perspective.c
@@ -21,15 +21,19 @@
 
 #include "ide-genesis-addin.h"
 #include "ide-genesis-perspective.h"
+#include "ide-gtk.h"
+#include "ide-workbench.h"
 
 struct _IdeGenesisPerspective
 {
   GtkBin            parent_instance;
 
+  GActionGroup     *actions;
   PeasExtensionSet *addins;
 
   GtkHeaderBar     *header_bar;
   GtkListBox       *list_box;
+  GtkWidget        *main_page;
   GtkStack         *stack;
 };
 
@@ -117,6 +121,9 @@ ide_genesis_perspective_addin_removed (PeasExtensionSet *set,
     {
       gpointer data = g_object_get_data (iter->data, "IDE_GENESIS_ADDIN");
 
+      if (data == NULL)
+        continue;
+
       if (data == (gpointer)exten)
         {
           gtk_container_remove (GTK_CONTAINER (self->list_box), iter->data);
@@ -172,19 +179,20 @@ ide_genesis_perspective_constructed (GObject *object)
                     G_CALLBACK (ide_genesis_perspective_addin_added),
                     self);
   g_signal_connect (self->addins,
-                    "extension-rmeoved",
+                    "extension-removed",
                     G_CALLBACK (ide_genesis_perspective_addin_removed),
                     self);
 }
 
 static void
-ide_genesis_perspective_finalize (GObject *object)
+ide_genesis_perspective_destroy (GtkWidget *widget)
 {
-  IdeGenesisPerspective *self = (IdeGenesisPerspective *)object;
+  IdeGenesisPerspective *self = (IdeGenesisPerspective *)widget;
 
+  g_clear_object (&self->actions);
   g_clear_object (&self->addins);
 
-  G_OBJECT_CLASS (ide_genesis_perspective_parent_class)->finalize (object);
+  GTK_WIDGET_CLASS (ide_genesis_perspective_parent_class)->destroy (widget);
 }
 
 static void
@@ -194,11 +202,13 @@ ide_genesis_perspective_class_init (IdeGenesisPerspectiveClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->constructed = ide_genesis_perspective_constructed;
-  object_class->finalize = ide_genesis_perspective_finalize;
+
+  widget_class->destroy = ide_genesis_perspective_destroy;
 
   gtk_widget_class_set_css_name (widget_class, "genesisperspective");
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-genesis-perspective.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeGenesisPerspective, list_box);
+  gtk_widget_class_bind_template_child (widget_class, IdeGenesisPerspective, main_page);
   gtk_widget_class_bind_template_child (widget_class, IdeGenesisPerspective, stack);
   gtk_widget_class_bind_template_child (widget_class, IdeGenesisPerspective, header_bar);
 }
@@ -230,15 +240,62 @@ ide_genesis_perspective_is_early (IdePerspective *perspective)
 static GtkWidget *
 ide_genesis_perspective_get_titlebar (IdePerspective *perspective)
 {
-  g_return_val_if_fail (IDE_IS_GENESIS_PERSPECTIVE (perspective), NULL);
+  g_assert (IDE_IS_GENESIS_PERSPECTIVE (perspective));
 
   return GTK_WIDGET (IDE_GENESIS_PERSPECTIVE (perspective)->header_bar);
 }
 
 static void
+go_previous (GSimpleAction *action,
+             GVariant      *variant,
+             gpointer       user_data)
+{
+  IdeGenesisPerspective *self = user_data;
+  IdeWorkbench *workbench;
+  GtkWidget *visible_child;
+
+  g_assert (IDE_IS_GENESIS_PERSPECTIVE (self));
+
+  visible_child = gtk_stack_get_visible_child (self->stack);
+
+  if (visible_child != self->main_page)
+    {
+      gtk_stack_set_visible_child (self->stack, self->main_page);
+      return;
+    }
+
+  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+  ide_workbench_set_visible_perspective_name (workbench, "greeter");
+}
+
+static GActionGroup *
+ide_genesis_perspective_get_actions (IdePerspective *perspective)
+{
+  IdeGenesisPerspective *self = (IdeGenesisPerspective *)perspective;
+
+  g_assert (IDE_IS_GENESIS_PERSPECTIVE (self));
+
+  if (self->actions == NULL)
+    {
+      const GActionEntry entries[] = {
+        { "go-previous", go_previous },
+      };
+
+      self->actions = G_ACTION_GROUP (g_simple_action_group_new ());
+      g_action_map_add_action_entries (G_ACTION_MAP (self->actions),
+                                       entries, G_N_ELEMENTS (entries), self);
+    }
+
+  g_assert (G_IS_ACTION_GROUP (self->actions));
+
+  return self->actions;
+}
+
+static void
 perspective_iface_init (IdePerspectiveInterface *iface)
 {
   iface->get_id = ide_genesis_perspective_get_id;
   iface->is_early = ide_genesis_perspective_is_early;
   iface->get_titlebar = ide_genesis_perspective_get_titlebar;
+  iface->get_actions = ide_genesis_perspective_get_actions;
 }
diff --git a/plugins/git/ide-git-clone-widget.c b/plugins/git/ide-git-clone-widget.c
index 376c4e5..146196f 100644
--- a/plugins/git/ide-git-clone-widget.c
+++ b/plugins/git/ide-git-clone-widget.c
@@ -16,58 +16,127 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <glib/gi18n.h>
+#include <libgit2-glib/ggit.h>
+#include <ide.h>
+
+#include "egg-animation.h"
+
 #include "ide-git-clone-widget.h"
+#include "ide-git-remote-callbacks.h"
+
+#define ANIMATION_DURATION_MSEC 250
 
 struct _IdeGitCloneWidget
 {
-  GtkBin parent_instance;
+  GtkBin                parent_instance;
+
+  GtkFileChooserButton *clone_location_button;
+  GtkEntry             *clone_location_entry;
+  GtkEntry             *clone_uri_entry;
+  GtkButton            *clone_button;
+  GtkLabel             *clone_error_label;
+  GtkProgressBar       *clone_progress;
+  GtkSpinner           *clone_spinner;
 };
 
-G_DEFINE_TYPE (IdeGitCloneWidget, ide_git_clone_widget, GTK_TYPE_BIN)
+typedef struct
+{
+  gchar *uri;
+  GFile *location;
+} CloneRequest;
 
-enum {
-  PROP_0,
-  LAST_PROP
-};
+G_DEFINE_TYPE (IdeGitCloneWidget, ide_git_clone_widget, GTK_TYPE_BIN)
 
-static GParamSpec *properties [LAST_PROP];
+static void ide_git_clone_widget_begin_clone (IdeGitCloneWidget *self);
 
 static void
-ide_git_clone_widget_finalize (GObject *object)
+clone_request_free (gpointer data)
 {
-  IdeGitCloneWidget *self = (IdeGitCloneWidget *)object;
+  CloneRequest *req = data;
 
-  G_OBJECT_CLASS (ide_git_clone_widget_parent_class)->finalize (object);
+  if (req != NULL)
+    {
+      g_clear_pointer (&req->uri, g_free);
+      g_clear_object (&req->location);
+      g_slice_free (CloneRequest, req);
+    }
+}
+
+static CloneRequest *
+clone_request_new (const gchar *uri,
+                   GFile       *location)
+{
+  CloneRequest *req;
+
+  g_assert (uri);
+  g_assert (location);
+
+  req = g_slice_new0 (CloneRequest);
+  req->uri = g_strdup (uri);
+  req->location = g_object_ref (location);
+
+  return req;
 }
 
 static void
-ide_git_clone_widget_get_property (GObject    *object,
-                                   guint       prop_id,
-                                   GValue     *value,
-                                   GParamSpec *pspec)
+ide_git_clone_widget_uri_changed (IdeGitCloneWidget *self,
+                                  GtkEntry          *entry)
 {
-  IdeGitCloneWidget *self = IDE_GIT_CLONE_WIDGET (object);
+  g_autoptr(IdeVcsUri) uri = NULL;
+  const gchar *text;
+
+  g_assert (IDE_IS_GIT_CLONE_WIDGET (self));
+  g_assert (GTK_IS_ENTRY (entry));
+
+  text = gtk_entry_get_text (entry);
+  uri = ide_vcs_uri_new (text);
+
+  if (uri != NULL)
+    {
+      const gchar *path;
+      gchar *name = NULL;
+
+      g_object_set (self->clone_uri_entry,
+                    "secondary-icon-name", NULL,
+                    "secondary-icon-tooltip-text", NULL,
+                    NULL);
+
+      path = ide_vcs_uri_get_path (uri);
 
-  switch (prop_id)
+      if (path != NULL)
+        {
+          name = g_path_get_basename (path);
+          if (g_str_has_suffix (name, ".git"))
+            *(strrchr (name, '.')) = '\0';
+          if (!g_str_equal (name, "/"))
+            gtk_entry_set_text (self->clone_location_entry, name);
+          g_free (name);
+        }
+    }
+  else
     {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      g_object_set (self->clone_uri_entry,
+                    "secondary-icon-name", "dialog-warning-symbolic",
+                    "secondary-icon-tooltip-text", _("A valid Git URL is required"),
+                    NULL);
     }
 }
 
 static void
-ide_git_clone_widget_set_property (GObject      *object,
-                                   guint         prop_id,
-                                   const GValue *value,
-                                   GParamSpec   *pspec)
+ide_git_clone_widget_clone_button_clicked (IdeGitCloneWidget *self,
+                                           GtkButton         *button)
 {
-  IdeGitCloneWidget *self = IDE_GIT_CLONE_WIDGET (object);
+  g_assert (IDE_IS_GIT_CLONE_WIDGET (self));
+  g_assert (GTK_IS_BUTTON (button));
 
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
+  ide_git_clone_widget_begin_clone (self);
+}
+
+static void
+ide_git_clone_widget_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (ide_git_clone_widget_parent_class)->finalize (object);
 }
 
 static void
@@ -77,15 +146,188 @@ ide_git_clone_widget_class_init (IdeGitCloneWidgetClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->finalize = ide_git_clone_widget_finalize;
-  object_class->get_property = ide_git_clone_widget_get_property;
-  object_class->set_property = ide_git_clone_widget_set_property;
 
   gtk_widget_class_set_css_name (widget_class, "gitclonewidget");
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/git/ide-git-clone-widget.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeGitCloneWidget, clone_button);
+  gtk_widget_class_bind_template_child (widget_class, IdeGitCloneWidget, clone_error_label);
+  gtk_widget_class_bind_template_child (widget_class, IdeGitCloneWidget, clone_location_button);
+  gtk_widget_class_bind_template_child (widget_class, IdeGitCloneWidget, clone_location_entry);
+  gtk_widget_class_bind_template_child (widget_class, IdeGitCloneWidget, clone_progress);
+  gtk_widget_class_bind_template_child (widget_class, IdeGitCloneWidget, clone_spinner);
+  gtk_widget_class_bind_template_child (widget_class, IdeGitCloneWidget, clone_uri_entry);
 }
 
 static void
 ide_git_clone_widget_init (IdeGitCloneWidget *self)
 {
+  gchar *projects_dir;
+
   gtk_widget_init_template (GTK_WIDGET (self));
+
+  projects_dir = g_build_filename (g_get_home_dir (), _("Projects"), NULL);
+  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (self->clone_location_button),
+                                       projects_dir);
+  g_free (projects_dir);
+
+  g_signal_connect_object (self->clone_uri_entry,
+                           "changed",
+                           G_CALLBACK (ide_git_clone_widget_uri_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->clone_button,
+                           "clicked",
+                           G_CALLBACK (ide_git_clone_widget_clone_button_clicked),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
+static gboolean
+open_after_timeout (gpointer user_data)
+{
+  IdeGitCloneWidget *self;
+  IdeWorkbench *workbench;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GFile) file = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (G_IS_TASK (task));
+
+  self = g_task_get_source_object (task);
+  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+
+  g_assert (IDE_IS_GIT_CLONE_WIDGET (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  gtk_widget_hide (GTK_WIDGET (self->clone_spinner));
+
+  file = g_task_propagate_pointer (task, &error);
+
+  if (error)
+    {
+      g_warning ("%s", error->message);
+      gtk_label_set_label (self->clone_error_label, error->message);
+      gtk_widget_show (GTK_WIDGET (self->clone_error_label));
+    }
+  else
+    {
+      //g_signal_emit (self, signals [OPEN_PROJECT], 0, file);
+      ide_workbench_set_visible_perspective_name (workbench, "editor");
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+ide_git_clone_widget_clone_cb (GObject      *object,
+                               GAsyncResult *result,
+                               gpointer      user_data)
+{
+  IdeGitCloneWidget *self = (IdeGitCloneWidget *)object;
+  GTask *task = (GTask *)result;
+
+  g_assert (IDE_IS_GIT_CLONE_WIDGET (self));
+  g_assert (G_IS_TASK (task));
+
+  egg_object_animate_full (self->clone_progress,
+                           EGG_ANIMATION_EASE_IN_OUT_QUAD,
+                           ANIMATION_DURATION_MSEC,
+                           NULL,
+                           (GDestroyNotify)ide_widget_hide_with_fade,
+                           self->clone_progress,
+                           "fraction", 1.0,
+                           NULL);
+
+  /*
+   * Wait for a second so animations can complete before opening
+   * the project. Otherwise, it's pretty jarring to the user.
+   */
+  g_timeout_add (ANIMATION_DURATION_MSEC, open_after_timeout, g_object_ref (task));
+}
+
+static void
+ide_git_clone_widget_worker (GTask        *task,
+                             gpointer      source_object,
+                             gpointer      task_data,
+                             GCancellable *cancellable)
+{
+  IdeGitCloneWidget *self = source_object;
+  GgitRepository *repository;
+  g_autoptr(GFile) workdir = NULL;
+  CloneRequest *req = task_data;
+  GgitCloneOptions *clone_options;
+  GgitFetchOptions *fetch_options;
+  GgitRemoteCallbacks *callbacks;
+  IdeProgress *progress;
+  GError *error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_GIT_CLONE_WIDGET (self));
+  g_assert (req != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  callbacks = g_object_new (IDE_TYPE_GIT_REMOTE_CALLBACKS, NULL);
+  progress = ide_git_remote_callbacks_get_progress (IDE_GIT_REMOTE_CALLBACKS (callbacks));
+  g_object_bind_property (progress, "fraction", self->clone_progress, "fraction", 0);
+
+  fetch_options = ggit_fetch_options_new ();
+  ggit_fetch_options_set_remote_callbacks (fetch_options, callbacks);
+
+  clone_options = ggit_clone_options_new ();
+  ggit_clone_options_set_is_bare (clone_options, FALSE);
+  ggit_clone_options_set_checkout_branch (clone_options, "master");
+  ggit_clone_options_set_fetch_options (clone_options, fetch_options);
+  g_clear_pointer (&fetch_options, ggit_fetch_options_free);
+
+  repository = ggit_repository_clone (req->uri, req->location, clone_options, &error);
+
+  g_clear_object (&callbacks);
+  g_clear_object (&clone_options);
+
+  if (repository == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  workdir = ggit_repository_get_workdir (repository);
+  g_task_return_pointer (task, g_object_ref (workdir), g_object_unref);
+
+  g_clear_object (&repository);
+}
+
+static void
+ide_git_clone_widget_begin_clone (IdeGitCloneWidget *self)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GFile) location = NULL;
+  g_autoptr(GFile) child = NULL;
+  CloneRequest *req;
+  const gchar *uri;
+  const gchar *child_name;
+
+  g_assert (IDE_IS_GIT_CLONE_WIDGET (self));
+
+  gtk_widget_set_sensitive (GTK_WIDGET (self->clone_button), FALSE);
+  gtk_label_set_label (self->clone_error_label, NULL);
+  gtk_widget_show (GTK_WIDGET (self->clone_spinner));
+
+  uri = gtk_entry_get_text (self->clone_uri_entry);
+  child_name = gtk_entry_get_text (self->clone_location_entry);
+  location = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (self->clone_location_button));
+
+  if (child_name != NULL)
+    {
+      child = g_file_get_child (location, child_name);
+      req = clone_request_new (uri, child);
+    }
+  else
+    {
+      req = clone_request_new (uri, location);
+    }
+
+  task = g_task_new (self, NULL, ide_git_clone_widget_clone_cb, self);
+  g_task_set_task_data (task, req, clone_request_free);
+  g_task_run_in_thread (task, ide_git_clone_widget_worker);
 }
diff --git a/plugins/git/ide-git-clone-widget.ui b/plugins/git/ide-git-clone-widget.ui
index 6f7739b..a7a2882 100644
--- a/plugins/git/ide-git-clone-widget.ui
+++ b/plugins/git/ide-git-clone-widget.ui
@@ -50,7 +50,7 @@
                         <property name="hexpand">true</property>
                         <property name="width-chars">35</property>
                         <property name="placeholder-text" translatable="yes">user 
host:repository.git</property>
-                        <property name="tooltip-text" translatable="yes">Enter the URL of your project's 
source code repository.</property>
+                        <property name="tooltip-text" translatable="yes">Enter the URL of your project's 
source code repository</property>
                         <property name="valign">baseline</property>
                         <property name="visible">true</property>
                       </object>
@@ -78,6 +78,7 @@
                           <object class="GtkEntry" id="clone_location_entry">
                             <property name="hexpand">true</property>
                             <property name="placeholder-text" translatable="yes">Directory</property>
+                            <property name="tooltip-text" translatable="yes">Enter the name of the directory 
to create</property>
                             <property name="valign">baseline</property>
                             <property name="visible">true</property>
                           </object>
@@ -128,15 +129,26 @@
               </object>
             </child>
             <child>
+              <object class="GtkLabel">
+                <property name="visible">true</property>
+                <property name="vexpand">true</property>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
               <object class="GtkLabel" id="clone_error_label">
                 <property name="valign">start</property>
                 <property name="vexpand">true</property>
-                <property name="visible">false</property>
+                <property name="visible">true</property>
                 <style>
                   <class name="error-label"/>
                 </style>
               </object>
               <packing>
+                <property name="position">1</property>
                 <property name="pack-type">end</property>
               </packing>
             </child>
@@ -150,6 +162,23 @@
               </object>
               <packing>
                 <property name="pack-type">end</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="clone_button">
+                <property name="halign">center</property>
+                <property name="visible">true</property>
+                <property name="margin-top">24</property>
+                <property name="label" translatable="yes">Cl_one Repository</property>
+                <property name="use-underline">true</property>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+                <property name="position">3</property>
               </packing>
             </child>
           </object>
diff --git a/plugins/git/ide-git-genesis-addin.c b/plugins/git/ide-git-genesis-addin.c
index 1cc812c..e738b26 100644
--- a/plugins/git/ide-git-genesis-addin.c
+++ b/plugins/git/ide-git-genesis-addin.c
@@ -53,7 +53,7 @@ ide_git_genesis_addin_get_icon_name (IdeGenesisAddin *addin)
 static gchar *
 ide_git_genesis_addin_get_title (IdeGenesisAddin *addin)
 {
-  return g_strdup (_("From a Git source code repository"));
+  return g_strdup (_("From a existing project in a Git repository"));
 }
 
 static GtkWidget *


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