[gnome-builder] GbTerminalView: save and save-as



commit 89869c81960dba944d3397e64c5d8ce6d6ac6daf
Author: S├ębastien Lafargue <slafargue gnome org>
Date:   Sun Jul 5 13:27:56 2015 +0200

    GbTerminalView: save and save-as
    
    Terminal views now support save
    (it do a save-as if you never saved it before)
    and save-as.
    
    The saved terminal is the focused one.
    The whole terminal backlog history is saved, unless
    there's a selection, in this case, the selection is saved.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=751503

 plugins/terminal/Makefile.am                |    3 +
 plugins/terminal/gb-terminal-view-actions.c |  362 +++++++++++++++++++++++++++
 plugins/terminal/gb-terminal-view-actions.h |   30 +++
 plugins/terminal/gb-terminal-view-private.h |   55 ++++
 plugins/terminal/gb-terminal-view.c         |   30 +--
 5 files changed, 460 insertions(+), 20 deletions(-)
---
diff --git a/plugins/terminal/Makefile.am b/plugins/terminal/Makefile.am
index 816233b..2039cc9 100644
--- a/plugins/terminal/Makefile.am
+++ b/plugins/terminal/Makefile.am
@@ -16,6 +16,9 @@ libterminal_la_SOURCES = \
        gb-terminal-private.h \
        gb-terminal-view.c \
        gb-terminal-view.h \
+       gb-terminal-view-private.h \
+       gb-terminal-view-actions.c \
+       gb-terminal-view-actions.h \
        gb-terminal-workbench-addin.c \
        gb-terminal-workbench-addin.h \
        $(NULL)
diff --git a/plugins/terminal/gb-terminal-view-actions.c b/plugins/terminal/gb-terminal-view-actions.c
new file mode 100644
index 0000000..3b6396f
--- /dev/null
+++ b/plugins/terminal/gb-terminal-view-actions.c
@@ -0,0 +1,362 @@
+/* gb-editor-view-actions.c
+ *
+ * Copyright (C) 2015 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gb-terminal-view"
+
+#include <glib/gi18n.h>
+#include <ide.h>
+#include <string.h>
+
+#include "gb-terminal-view-actions.h"
+#include "gb-terminal-view-private.h"
+#include "gb-view-grid.h"
+#include "gb-widget.h"
+#include "gb-workbench.h"
+
+typedef struct
+{
+  VteTerminal    *terminal;
+  GFile          *file;
+  GOutputStream  *stream;
+} SaveTask;
+
+static void
+savetask_free (gpointer data)
+{
+  SaveTask *savetask = (SaveTask *)data;
+
+  if (savetask != NULL)
+    {
+      if (savetask->file)
+        g_object_unref (savetask->file);
+
+      g_object_unref (savetask->stream);
+      g_object_unref (savetask->terminal);
+    }
+}
+
+gboolean
+gb_terminal_view_actions_save_finish (GbTerminalView  *view,
+                                      GAsyncResult    *result,
+                                      GError         **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (g_task_is_valid (result, view), FALSE);
+
+  g_return_val_if_fail (GB_IS_TERMINAL_VIEW (view), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+  g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static void
+save_async (GTask        *task,
+            gpointer      source_object,
+            gpointer      task_data,
+            GCancellable *cancellable)
+{
+  GbTerminalView *view = source_object;
+  SaveTask *savetask = (SaveTask *)task_data;
+  GError *error = NULL;
+  gboolean ret;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (GB_IS_TERMINAL_VIEW (view));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (view->selection_buffer != NULL)
+    {
+      g_autoptr(GInputStream) input_stream = NULL;
+
+      input_stream = g_memory_input_stream_new_from_data (view->selection_buffer, -1, NULL);
+      ret = g_output_stream_splice (G_OUTPUT_STREAM (savetask->stream),
+                                    G_INPUT_STREAM (input_stream),
+                                    G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                                    cancellable,
+                                    &error);
+
+      g_clear_pointer (&view->selection_buffer, g_free);
+    }
+  else
+    {
+      ret = vte_terminal_write_contents_sync (savetask->terminal,
+                                              G_OUTPUT_STREAM (savetask->stream),
+                                              VTE_WRITE_DEFAULT,
+                                              cancellable,
+                                              &error);
+    }
+
+  if (ret)
+    g_task_return_boolean (task, TRUE);
+  else
+    g_task_return_error (task, error);
+}
+
+static void
+gb_terminal_view_actions_save_async (GbTerminalView       *view,
+                                     VteTerminal          *terminal,
+                                     GFile                *file,
+                                     GAsyncReadyCallback   callback,
+                                     GCancellable         *cancellable,
+                                     gpointer              user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GFileOutputStream) output_stream = NULL;
+  SaveTask *savetask;
+  GError *error = NULL;
+
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (view, cancellable, callback, user_data);
+
+  output_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, cancellable, &error);
+  if (output_stream)
+    {
+      savetask = g_slice_new0 (SaveTask);
+      savetask->file = g_object_ref (file);
+      savetask->stream = g_object_ref (output_stream);
+      savetask->terminal = g_object_ref (terminal);
+
+      g_task_set_task_data (task, savetask, savetask_free);
+      g_task_run_in_thread (task, save_async);
+    }
+  else
+    g_task_return_error (task, error);
+}
+
+static void
+save_as_cb (GObject      *object,
+            GAsyncResult *result,
+            gpointer      user_data)
+{
+  GTask *task = (GTask *)result;
+  GbTerminalView *view = user_data;
+  SaveTask *savetask;
+  GFile *file;
+  GError *error = NULL;
+
+  savetask = g_task_get_task_data (task);
+  file = g_object_ref (savetask->file);
+
+  if (!gb_terminal_view_actions_save_finish (view, result, &error))
+    {
+      g_object_unref (file);
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+    }
+  else
+    {
+      if (view->bottom_has_focus && view->terminal_bottom != NULL)
+        {
+          g_clear_object (&view->save_as_file_bottom);
+          view->save_as_file_bottom = file;
+        }
+      else
+        {
+          g_clear_object (&view->save_as_file_top);
+          view->save_as_file_top = file;
+        }
+    }
+}
+
+static void
+save_cb (GObject      *object,
+         GAsyncResult *result,
+         gpointer      user_data)
+{
+  GbTerminalView *view = user_data;
+  GError *error = NULL;
+
+  if (!gb_terminal_view_actions_save_finish (view, result, &error))
+    {
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+    }
+}
+
+static GFile *
+get_last_focused_terminal_file (GbTerminalView *view)
+{
+  GFile *file = NULL;
+
+  if (view->bottom_has_focus)
+    {
+      if (G_IS_FILE (view->save_as_file_bottom))
+        file = view->save_as_file_bottom;
+    }
+  else if G_IS_FILE (view->save_as_file_top)
+    file = view->save_as_file_top;
+
+  return file;
+}
+
+static VteTerminal *
+get_last_focused_terminal (GbTerminalView *view)
+{
+  VteTerminal *terminal;
+
+  if (view->bottom_has_focus && view->terminal_bottom != NULL)
+    terminal = view->terminal_bottom;
+  else
+    terminal = view->terminal_top;
+
+  return terminal;
+}
+
+static gchar *
+gb_terminal_get_selected_text (GbTerminalView *view, VteTerminal **terminal_p)
+{
+  VteTerminal *terminal;
+  gchar *buf = NULL;
+
+  terminal = get_last_focused_terminal (view);
+  if (terminal_p != NULL)
+    *terminal_p = terminal;
+
+  if (vte_terminal_get_has_selection (terminal))
+    {
+      vte_terminal_copy_primary (terminal);
+      buf = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
+    }
+
+  return buf;
+}
+
+static void
+save_as_response (GtkWidget *widget,
+                  gint       response,
+                  gpointer   user_data)
+{
+  g_autoptr(GbTerminalView) view = user_data;
+  g_autoptr(GFile) file = NULL;
+  GtkFileChooser *chooser = (GtkFileChooser *)widget;
+  VteTerminal *terminal;
+
+  g_assert (GTK_IS_FILE_CHOOSER (chooser));
+  g_assert (GB_IS_TERMINAL_VIEW (view));
+
+  switch (response)
+    {
+    case GTK_RESPONSE_OK:
+      file = gtk_file_chooser_get_file (chooser);
+      terminal = get_last_focused_terminal (view);
+      gb_terminal_view_actions_save_async (view, terminal, file, save_as_cb, NULL, view);
+      break;
+
+    case GTK_RESPONSE_CANCEL:
+      g_free (view->selection_buffer);
+
+    default:
+      break;
+    }
+
+  gtk_widget_destroy (widget);
+}
+
+static void
+gb_terminal_view_actions_save_as (GSimpleAction *action,
+                                  GVariant      *param,
+                                  gpointer       user_data)
+{
+  GbTerminalView *view = user_data;
+  GtkWidget *suggested;
+  GtkWidget *toplevel;
+  GtkWidget *dialog;
+  GFile *file = NULL;
+
+  g_assert (GB_IS_TERMINAL_VIEW (view));
+
+  /* We can't get this later because the dialog makes the terminal
+   * unfocused and thus resets the selection
+   */
+  view->selection_buffer = gb_terminal_get_selected_text (view, NULL);
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+  dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
+                         "action", GTK_FILE_CHOOSER_ACTION_SAVE,
+                         "do-overwrite-confirmation", TRUE,
+                         "local-only", FALSE,
+                         "modal", TRUE,
+                         "select-multiple", FALSE,
+                         "show-hidden", FALSE,
+                         "transient-for", toplevel,
+                         "title", _("Save Terminal content As"),
+                         NULL);
+
+  file = get_last_focused_terminal_file (view);
+  if (file != NULL)
+    gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file, 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);
+
+  g_signal_connect (dialog, "response", G_CALLBACK (save_as_response), g_object_ref (view));
+
+  gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+gb_terminal_view_actions_save (GSimpleAction *action,
+                               GVariant      *param,
+                               gpointer       user_data)
+{
+  GbTerminalView *view = user_data;
+  VteTerminal *terminal;
+  GFile *file = NULL;
+
+  g_assert (GB_IS_TERMINAL_VIEW (view));
+
+  file = get_last_focused_terminal_file (view);
+  if (file != NULL)
+    {
+      /* We can't get this later because the dialog make the terminal
+       * unfocused and thus reset the selection
+       */
+      view->selection_buffer = gb_terminal_get_selected_text (view, &terminal);
+      gb_terminal_view_actions_save_async (view, terminal, file, save_cb, NULL, view);
+    }
+  else
+    {
+      gb_terminal_view_actions_save_as (action, param, user_data);
+    }
+}
+
+static GActionEntry GbTerminalViewActions[] = {
+  { "save", gb_terminal_view_actions_save },
+  { "save-as", gb_terminal_view_actions_save_as },
+};
+
+void
+gb_terminal_view_actions_init (GbTerminalView *self)
+{
+  g_autoptr(GSimpleActionGroup) group = NULL;
+
+  group = g_simple_action_group_new ();
+  g_action_map_add_action_entries (G_ACTION_MAP (group), GbTerminalViewActions,
+                                   G_N_ELEMENTS (GbTerminalViewActions), self);
+  gtk_widget_insert_action_group (GTK_WIDGET (self), "view", G_ACTION_GROUP (group));
+}
diff --git a/plugins/terminal/gb-terminal-view-actions.h b/plugins/terminal/gb-terminal-view-actions.h
new file mode 100644
index 0000000..3d7c407
--- /dev/null
+++ b/plugins/terminal/gb-terminal-view-actions.h
@@ -0,0 +1,30 @@
+/* gb-terminal-view-actions.h
+ *
+ * opyright (C) 2015 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_TERMINAL_VIEW_ACTIONS_H
+#define GB_TERMINAL_VIEW_ACTIONS_H
+
+#include "gb-terminal-view.h"
+
+G_BEGIN_DECLS
+
+void gb_terminal_view_actions_init   (GbTerminalView *self);
+
+G_END_DECLS
+
+#endif /* GB_TERMINAL_VIEW_ACTIONS_H */
diff --git a/plugins/terminal/gb-terminal-view-private.h b/plugins/terminal/gb-terminal-view-private.h
new file mode 100644
index 0000000..23f014a
--- /dev/null
+++ b/plugins/terminal/gb-terminal-view-private.h
@@ -0,0 +1,55 @@
+/* gb-terminal-view-private.h
+ *
+ * Copyright (C) 2015 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_TERMINAL_VIEW_PRIVATE_H
+#define GB_TERMINAL_VIEW_PRIVATE_H
+
+#include <vte/vte.h>
+
+#include "gb-terminal-document.h"
+#include "gb-view.h"
+
+G_BEGIN_DECLS
+
+struct _GbTerminalView
+{
+  GbView               parent_instance;
+
+  GbTerminalDocument  *document;
+
+  VteTerminal         *terminal_top;
+  VteTerminal         *terminal_bottom;
+
+  GFile               *save_as_file_top;
+  GFile               *save_as_file_bottom;
+
+  gchar               *selection_buffer;
+
+  GtkWidget           *scrolled_window_bottom;
+
+  guint                top_has_spawned : 1;
+  guint                bottom_has_spawned : 1;
+  guint                bottom_has_focus : 1;
+
+  guint                top_has_needs_attention : 1;
+  guint                bottom_has_needs_attention : 1;
+};
+
+G_END_DECLS
+
+#endif /* GB_TERMINAL_VIEW_PRIVATE_H */
diff --git a/plugins/terminal/gb-terminal-view.c b/plugins/terminal/gb-terminal-view.c
index aab6952..80876c3 100644
--- a/plugins/terminal/gb-terminal-view.c
+++ b/plugins/terminal/gb-terminal-view.c
@@ -1,4 +1,4 @@
-/* gb-terminal.c
+/* gb-terminal-view.c
  *
  * Copyright (C) 2015 Christian Hergert <christian hergert me>
  *
@@ -22,29 +22,12 @@
 
 #include "gb-terminal-document.h"
 #include "gb-terminal-view.h"
+#include "gb-terminal-view-private.h"
+#include "gb-terminal-view-actions.h"
 #include "gb-view.h"
 #include "gb-widget.h"
 #include "gb-workbench.h"
 
-struct _GbTerminalView
-{
-  GbView               parent_instance;
-
-  GbTerminalDocument  *document;
-
-  VteTerminal         *terminal_top;
-  VteTerminal         *terminal_bottom;
-
-  GtkWidget           *scrolled_window_bottom;
-
-  guint                top_has_spawned : 1;
-  guint                bottom_has_spawned : 1;
-  guint                bottom_has_focus : 1;
-
-  guint                top_has_needs_attention : 1;
-  guint                bottom_has_needs_attention : 1;
-};
-
 G_DEFINE_TYPE (GbTerminalView, gb_terminal_view, GB_TYPE_VIEW)
 
 enum {
@@ -477,7 +460,10 @@ gb_terminal_set_split_view (GbView   *view,
       gtk_widget_hide (self->scrolled_window_bottom);
 
       self->terminal_bottom = NULL;
+      self->bottom_has_focus = FALSE;
       self->bottom_has_spawned = FALSE;
+      self->bottom_has_needs_attention = FALSE;
+      g_clear_object (&self->save_as_file_bottom);
       gtk_widget_grab_focus (GTK_WIDGET (self->terminal_top));
     }
 }
@@ -546,6 +532,9 @@ gb_terminal_view_finalize (GObject *object)
   GbTerminalView *self = GB_TERMINAL_VIEW (object);
 
   g_clear_object (&self->document);
+  g_clear_object (&self->save_as_file_top);
+  g_clear_object (&self->save_as_file_bottom);
+  g_clear_pointer (&self->selection_buffer, g_free);
 
   G_OBJECT_CLASS (gb_terminal_view_parent_class)->finalize (object);
 }
@@ -645,6 +634,7 @@ gb_terminal_view_init (GbTerminalView *self)
   gtk_widget_init_template (GTK_WIDGET (self));
 
   gb_terminal_view_connect_terminal (self, self->terminal_top);
+  gb_terminal_view_actions_init (self);
 
   /*
    * FIXME: Should we allow setting the terminal font independently from editor?


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