[gnome-builder] GbTerminalView: save and save-as
- From: Sebastien Lafargue <slafargue src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] GbTerminalView: save and save-as
- Date: Tue, 7 Jul 2015 19:15:34 +0000 (UTC)
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]