[gnome-builder] editor: add save-as action using GtkSourceFileSaver.



commit ff72abbbb416a2264abdedddf20657d87f88786c
Author: Christian Hergert <christian hergert me>
Date:   Mon Sep 8 15:54:12 2014 -0700

    editor: add save-as action using GtkSourceFileSaver.

 src/editor/gb-editor-tab.c            |  195 +++++++++++++++++++++++++++++----
 src/editor/gb-editor-tab.h            |    1 +
 src/editor/gb-editor-workspace.c      |   24 ++++-
 src/resources/keybindings/default.ini |    1 +
 4 files changed, 195 insertions(+), 26 deletions(-)
---
diff --git a/src/editor/gb-editor-tab.c b/src/editor/gb-editor-tab.c
index f6633ce..a4b0ed3 100644
--- a/src/editor/gb-editor-tab.c
+++ b/src/editor/gb-editor-tab.c
@@ -105,6 +105,11 @@ struct _GbEditorTabPrivate
    * track our unsaved file number.
    */
   guint unsaved_number;
+
+  /*
+   * Animation for save progress.
+   */
+  GbAnimation *save_animation;
 };
 
 enum {
@@ -154,24 +159,6 @@ gb_editor_tab_get_file (GbEditorTab *tab)
   return tab->priv->file;
 }
 
-static void
-gb_editor_tab_set_file (GbEditorTab   *tab,
-                        GtkSourceFile *file)
-{
-  GbEditorTabPrivate *priv;
-
-  g_return_if_fail (GB_IS_EDITOR_TAB (tab));
-  g_return_if_fail (!file || GTK_SOURCE_IS_FILE (file));
-  g_return_if_fail (!tab->priv->file);
-
-  priv = tab->priv;
-
-  priv->file = file ? g_object_ref (file) : NULL;
-
-  g_object_notify_by_pspec (G_OBJECT (tab), gParamSpecs [PROP_FILE]);
-}
-
-
 /**
  * gb_editor_tab_go_to_start:
  * @tab: A #GbEditorTab.
@@ -707,6 +694,168 @@ gb_editor_tab_set_font_desc (GbEditorTab                *tab,
 }
 
 static void
+file_progress_cb (goffset      current_num_bytes,
+                  goffset      total_num_bytes,
+                  GbEditorTab *tab)
+{
+  GbEditorTabPrivate *priv;
+  gdouble fraction;
+
+  g_return_if_fail (GB_IS_EDITOR_TAB (tab));
+
+  priv = tab->priv;
+
+  if (priv->save_animation)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (priv->save_animation),
+                                    (gpointer *)&priv->save_animation);
+      gb_animation_stop (priv->save_animation);
+      priv->save_animation = NULL;
+    }
+
+  fraction = total_num_bytes
+           ? ((gdouble)current_num_bytes / (gdouble)total_num_bytes)
+           : 1.0;
+
+  priv->save_animation = gb_object_animate (priv->progress_bar,
+                                            GB_ANIMATION_LINEAR,
+                                            250,
+                                            NULL,
+                                            "fraction", fraction,
+                                            NULL);
+  g_object_add_weak_pointer (G_OBJECT (priv->save_animation),
+                             (gpointer *)&priv->save_animation);
+}
+
+static gboolean
+hide_progress_bar_cb (gpointer data)
+{
+  GbEditorTab *tab = data;
+
+  g_assert (GB_IS_EDITOR_TAB (tab));
+
+  gb_object_animate_full (tab->priv->progress_bar,
+                          GB_ANIMATION_EASE_OUT_CUBIC,
+                          250,
+                          NULL,
+                          (GDestroyNotify)gtk_widget_hide,
+                          tab->priv->progress_bar,
+                          "opacity", 0.0,
+                          NULL);
+
+  g_object_unref (tab);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+on_save_cb (GtkSourceFileSaver *saver,
+            GAsyncResult       *result,
+            GbEditorTab        *tab)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver));
+  g_return_if_fail (G_IS_ASYNC_RESULT (result));
+  g_return_if_fail (GB_IS_EDITOR_TAB (tab));
+
+  /*
+   * Hide the progress bar after a timeout period.
+   */
+  g_timeout_add (350, hide_progress_bar_cb, g_object_ref (tab));
+
+  if (!gtk_source_file_saver_save_finish (saver, result, &error))
+    {
+      /*
+       * TODO: Propagate error to tab.
+       */
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+    }
+
+  g_object_unref (tab);
+}
+
+static void
+gb_editor_tab_do_save (GbEditorTab *tab)
+{
+  GbEditorTabPrivate *priv;
+  GtkSourceFileSaver *saver;
+
+  g_return_if_fail (GB_IS_EDITOR_TAB (tab));
+  g_return_if_fail (tab->priv->file);
+
+  priv = tab->priv;
+
+  /*
+   * TODO: Tab needs a state machine for what are valid operations.
+   */
+
+  gtk_progress_bar_set_fraction (priv->progress_bar, 0.0);
+  gtk_widget_set_opacity (GTK_WIDGET (priv->progress_bar), 1.0);
+  gtk_widget_show (GTK_WIDGET (priv->progress_bar));
+
+  saver = gtk_source_file_saver_new (GTK_SOURCE_BUFFER (priv->document),
+                                     priv->file);
+
+  gtk_source_file_saver_save_async (saver,
+                                    G_PRIORITY_DEFAULT,
+                                    NULL, /* TODO: Cancellable */
+                                    (GFileProgressCallback)file_progress_cb,
+                                    tab,
+                                    NULL,
+                                    (GAsyncReadyCallback)on_save_cb,
+                                    g_object_ref (tab));
+}
+
+void
+gb_editor_tab_save_as (GbEditorTab *tab)
+{
+  GtkFileChooserDialog *dialog;
+  GtkWidget *toplevel;
+  GtkWidget *suggested;
+  GtkResponseType response;
+
+  g_return_if_fail (GB_IS_EDITOR_TAB (tab));
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tab));
+
+  dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
+                         "action", GTK_FILE_CHOOSER_ACTION_SAVE,
+                         "do-overwrite-confirmation", TRUE,
+                         "local-only", FALSE,
+                         "select-multiple", FALSE,
+                         "show-hidden", FALSE,
+                         "transient-for", toplevel,
+                         "title", _("Save As"),
+                         NULL);
+
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                          _("Cancel"), GTK_RESPONSE_CANCEL,
+                          _("Save"), GTK_RESPONSE_OK,
+                          NULL);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+  suggested = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog),
+                                                  GTK_RESPONSE_OK);
+  gtk_style_context_add_class (gtk_widget_get_style_context (suggested),
+                               GTK_STYLE_CLASS_SUGGESTED_ACTION);
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  if (response == GTK_RESPONSE_OK)
+    {
+      GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+      gtk_source_file_set_location (tab->priv->file, file);
+      gb_editor_tab_do_save (tab);
+      g_clear_object (&file);
+    }
+
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
 gb_editor_tab_freeze_drag (GbTab *tab)
 {
   GbEditorTab *editor = (GbEditorTab *) tab;
@@ -1281,10 +1430,6 @@ gb_editor_tab_set_property (GObject      *object,
       gb_editor_tab_set_document (tab, g_value_get_object (value));
       break;
 
-    case PROP_FILE:
-      gb_editor_tab_set_file (tab, g_value_get_object (value));
-      break;
-
     case PROP_FONT_DESC:
       gb_editor_tab_set_font_desc (tab, g_value_get_boxed (value));
       break;
@@ -1332,8 +1477,7 @@ gb_editor_tab_class_init (GbEditorTabClass *klass)
                            _("File"),
                            _("The file for the tab."),
                            GTK_SOURCE_TYPE_FILE,
-                           (G_PARAM_READWRITE |
-                            G_PARAM_CONSTRUCT_ONLY |
+                           (G_PARAM_READABLE |
                             G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (object_class, PROP_FILE,
                                    gParamSpecs [PROP_FILE]);
@@ -1391,5 +1535,8 @@ static void
 gb_editor_tab_init (GbEditorTab *tab)
 {
   tab->priv = gb_editor_tab_get_instance_private (tab);
+
   gtk_widget_init_template (GTK_WIDGET (tab));
+
+  tab->priv->file = gtk_source_file_new ();
 }
diff --git a/src/editor/gb-editor-tab.h b/src/editor/gb-editor-tab.h
index e684cac..0d27520 100644
--- a/src/editor/gb-editor-tab.h
+++ b/src/editor/gb-editor-tab.h
@@ -63,6 +63,7 @@ void              gb_editor_tab_set_show_find (GbEditorTab                *tab,
 void              gb_editor_tab_reformat      (GbEditorTab                *tab);
 void              gb_editor_tab_go_to_end     (GbEditorTab                *tab);
 void              gb_editor_tab_go_to_start   (GbEditorTab                *tab);
+void              gb_editor_tab_save_as       (GbEditorTab                *tab);
 
 G_END_DECLS
 
diff --git a/src/editor/gb-editor-workspace.c b/src/editor/gb-editor-workspace.c
index 9b7fcf2..5931b48 100644
--- a/src/editor/gb-editor-workspace.c
+++ b/src/editor/gb-editor-workspace.c
@@ -104,6 +104,25 @@ on_reformat_activate (GSimpleAction *action,
 }
 
 static void
+on_save_as_activate (GSimpleAction *action,
+                     GVariant      *parameter,
+                     gpointer       user_data)
+{
+  GbEditorWorkspace *workspace = user_data;
+  GbTab *tab;
+
+  g_return_if_fail (GB_IS_EDITOR_WORKSPACE (workspace));
+
+  tab = gb_multi_notebook_get_active_tab (workspace->priv->multi_notebook);
+
+  if (tab)
+    {
+      g_assert (GB_IS_EDITOR_TAB (tab));
+      gb_editor_tab_save_as (GB_EDITOR_TAB (tab));
+    }
+}
+
+static void
 on_go_to_start_activate (GSimpleAction *action,
                          GVariant      *parameter,
                          gpointer       user_data)
@@ -185,9 +204,10 @@ static void
 gb_editor_workspace_init (GbEditorWorkspace *workspace)
 {
   static const GActionEntry action_entries[] = {
-    { "reformat", on_reformat_activate },
-    { "go-to-start", on_go_to_start_activate },
     { "go-to-end", on_go_to_end_activate },
+    { "go-to-start", on_go_to_start_activate },
+    { "reformat", on_reformat_activate },
+    { "save-as", on_save_as_activate },
   };
   GbEditorWorkspacePrivate *priv;
   GbNotebook *notebook;
diff --git a/src/resources/keybindings/default.ini b/src/resources/keybindings/default.ini
index 9b68607..6714a8f 100644
--- a/src/resources/keybindings/default.ini
+++ b/src/resources/keybindings/default.ini
@@ -6,3 +6,4 @@ find = <Control>F
 reformat = <Control><Shift>R
 go-to-end = <Control>Page_Down
 go-to-start = <Control>Page_Up
+save-as = <Control><Shift>S


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