gem-graph-client/libide/terminal/ide-terminal-page-actions.c

336 lines
10 KiB
C

/* gb-editor-view-actions.c
*
* Copyright 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/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "ide-terminal-page"
#include <glib/gi18n.h>
#include <libide-gui.h>
#include <libide-terminal.h>
#include <string.h>
#include "ide-terminal-page-actions.h"
#include "ide-terminal-page-private.h"
typedef struct
{
VteTerminal *terminal;
GFile *file;
GOutputStream *stream;
gchar *buffer;
} SaveTask;
static void
savetask_free (gpointer data)
{
SaveTask *savetask = (SaveTask *)data;
if (savetask != NULL)
{
g_clear_object (&savetask->file);
g_clear_object (&savetask->stream);
g_clear_object (&savetask->terminal);
g_clear_pointer (&savetask->buffer, g_free);
g_slice_free (SaveTask, savetask);
}
}
static gboolean
ide_terminal_page_actions_save_finish (IdeTerminalPage *view,
GAsyncResult *result,
GError **error)
{
IdeTask *task = (IdeTask *)result;
g_return_val_if_fail (ide_task_is_valid (result, view), FALSE);
g_return_val_if_fail (IDE_IS_TERMINAL_PAGE (view), FALSE);
g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
g_return_val_if_fail (IDE_IS_TASK (task), FALSE);
return ide_task_propagate_boolean (task, error);
}
static void
save_worker (IdeTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SaveTask *savetask = (SaveTask *)task_data;
g_autoptr(GError) error = NULL;
gboolean ret;
g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_TASK (task));
g_assert (IDE_IS_TERMINAL_PAGE (source_object));
g_assert (savetask != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
if (savetask->buffer != NULL)
{
g_autoptr(GInputStream) input_stream = NULL;
input_stream = g_memory_input_stream_new_from_data (savetask->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);
}
else
{
ret = vte_terminal_write_contents_sync (savetask->terminal,
G_OUTPUT_STREAM (savetask->stream),
VTE_WRITE_DEFAULT,
cancellable,
&error);
}
if (ret)
ide_task_return_boolean (task, TRUE);
else
ide_task_return_error (task, g_steal_pointer (&error));
}
static void
ide_terminal_page_actions_save_async (IdeTerminalPage *view,
VteTerminal *terminal,
GFile *file,
GAsyncReadyCallback callback,
GCancellable *cancellable,
gpointer user_data)
{
g_autoptr(IdeTask) task = NULL;
g_autoptr(GFileOutputStream) output_stream = NULL;
g_autoptr(GError) error = NULL;
SaveTask *savetask;
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_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 != NULL)
{
savetask = g_slice_new0 (SaveTask);
savetask->file = g_object_ref (file);
savetask->stream = g_object_ref (G_OUTPUT_STREAM (output_stream));
savetask->terminal = g_object_ref (terminal);
savetask->buffer = g_steal_pointer (&view->selection_buffer);
ide_task_set_task_data (task, savetask, savetask_free);
save_worker (task, view, savetask, cancellable);
}
else
ide_task_return_error (task, g_steal_pointer (&error));
}
static void
save_as_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
IdeTask *task = (IdeTask *)result;
IdeTerminalPage *view = user_data;
SaveTask *savetask;
GFile *file;
GError *error = NULL;
savetask = ide_task_get_task_data (task);
file = g_object_ref (savetask->file);
if (!ide_terminal_page_actions_save_finish (view, result, &error))
{
g_object_unref (file);
g_warning ("%s", error->message);
g_clear_error (&error);
}
else
{
g_clear_object (&view->save_as_file_top);
view->save_as_file_top = file;
}
}
static GFile *
get_last_focused_terminal_file (IdeTerminalPage *view)
{
GFile *file = NULL;
if (G_IS_FILE (view->save_as_file_top))
file = view->save_as_file_top;
return file;
}
static VteTerminal *
get_last_focused_terminal (IdeTerminalPage *view)
{
return VTE_TERMINAL (view->terminal_top);
}
static gchar *
gb_terminal_get_selected_text (IdeTerminalPage *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(IdeTerminalPage) view = user_data;
g_autoptr(GFile) file = NULL;
GtkFileChooser *chooser = (GtkFileChooser *)widget;
VteTerminal *terminal;
g_assert (GTK_IS_FILE_CHOOSER (chooser));
g_assert (IDE_IS_TERMINAL_PAGE (view));
switch (response)
{
case GTK_RESPONSE_OK:
file = gtk_file_chooser_get_file (chooser);
terminal = get_last_focused_terminal (view);
ide_terminal_page_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
ide_terminal_page_actions_save_as (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
IdeTerminalPage *view = user_data;
GtkWidget *suggested;
GtkWidget *toplevel;
GtkWidget *dialog;
GFile *file = NULL;
g_assert (IDE_IS_TERMINAL_PAGE (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));
ide_gtk_window_present (GTK_WINDOW (dialog));
}
static void
ide_terminal_page_actions_reset (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
IdeTerminalPage *self = user_data;
VteTerminal *terminal;
g_assert (G_IS_SIMPLE_ACTION (action));
g_assert (IDE_IS_TERMINAL_PAGE (self));
terminal = get_last_focused_terminal (self);
vte_terminal_reset (terminal, TRUE, FALSE);
}
static void
ide_terminal_page_actions_reset_and_clear (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
IdeTerminalPage *self = user_data;
VteTerminal *terminal;
g_assert (G_IS_SIMPLE_ACTION (action));
g_assert (IDE_IS_TERMINAL_PAGE (self));
terminal = get_last_focused_terminal (self);
vte_terminal_reset (terminal, TRUE, TRUE);
}
static GActionEntry IdeTerminalPageActions[] = {
{ "save-as", ide_terminal_page_actions_save_as },
{ "reset", ide_terminal_page_actions_reset },
{ "reset-and-clear", ide_terminal_page_actions_reset_and_clear },
};
void
ide_terminal_page_actions_init (IdeTerminalPage *self)
{
g_autoptr(GSimpleActionGroup) group = NULL;
group = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (group), IdeTerminalPageActions,
G_N_ELEMENTS (IdeTerminalPageActions), self);
gtk_widget_insert_action_group (GTK_WIDGET (self), "terminal-view", G_ACTION_GROUP (group));
}