summaryrefslogtreecommitdiff
path: root/pluma/pluma-commands-file.c
diff options
context:
space:
mode:
Diffstat (limited to 'pluma/pluma-commands-file.c')
-rwxr-xr-xpluma/pluma-commands-file.c1885
1 files changed, 1885 insertions, 0 deletions
diff --git a/pluma/pluma-commands-file.c b/pluma/pluma-commands-file.c
new file mode 100755
index 00000000..3e32a495
--- /dev/null
+++ b/pluma/pluma-commands-file.c
@@ -0,0 +1,1885 @@
+/*
+ * pluma-commands-file.c
+ * This file is part of pluma
+ *
+ * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
+ * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the pluma Team, 1998-2005. See the AUTHORS file for a
+ * list of people on the pluma Team.
+ * See the ChangeLog files for a list of changes.
+ *
+ * $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h> /* For strlen and strcmp */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "pluma-commands.h"
+#include "pluma-window.h"
+#include "pluma-window-private.h"
+#include "pluma-statusbar.h"
+#include "pluma-debug.h"
+#include "pluma-utils.h"
+#include "pluma-file-chooser-dialog.h"
+#include "dialogs/pluma-close-confirmation-dialog.h"
+
+
+/* Defined constants */
+#define PLUMA_OPEN_DIALOG_KEY "pluma-open-dialog-key"
+#define PLUMA_TAB_TO_SAVE_AS "pluma-tab-to-save-as"
+#define PLUMA_LIST_OF_TABS_TO_SAVE_AS "pluma-list-of-tabs-to-save-as"
+#define PLUMA_IS_CLOSING_ALL "pluma-is-closing-all"
+#define PLUMA_IS_QUITTING "pluma-is-quitting"
+#define PLUMA_IS_CLOSING_TAB "pluma-is-closing-tab"
+#define PLUMA_IS_QUITTING_ALL "pluma-is-quitting-all"
+
+static void tab_state_changed_while_saving (PlumaTab *tab,
+ GParamSpec *pspec,
+ PlumaWindow *window);
+
+void
+_pluma_cmd_file_new (GtkAction *action,
+ PlumaWindow *window)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+ pluma_window_create_tab (window, TRUE);
+}
+
+static PlumaTab *
+get_tab_from_file (GList *docs, GFile *file)
+{
+ PlumaTab *tab = NULL;
+
+ while (docs != NULL)
+ {
+ PlumaDocument *d;
+ GFile *l;
+
+ d = PLUMA_DOCUMENT (docs->data);
+
+ l = pluma_document_get_location (d);
+ if (l != NULL)
+ {
+ if (g_file_equal (l, file))
+ {
+ tab = pluma_tab_get_from_document (d);
+ g_object_unref (l);
+ break;
+ }
+
+ g_object_unref (l);
+ }
+
+ docs = g_list_next (docs);
+ }
+
+ return tab;
+}
+
+static gboolean
+is_duplicated_file (GSList *files, GFile *file)
+{
+ while (files != NULL)
+ {
+ if (g_file_equal (files->data, file))
+ return TRUE;
+
+ files = g_slist_next (files);
+ }
+
+ return FALSE;
+}
+
+/* File loading */
+static gint
+load_file_list (PlumaWindow *window,
+ GSList *files,
+ const PlumaEncoding *encoding,
+ gint line_pos,
+ gboolean create)
+{
+ PlumaTab *tab;
+ gint loaded_files = 0; /* Number of files to load */
+ gboolean jump_to = TRUE; /* Whether to jump to the new tab */
+ GList *win_docs;
+ GSList *files_to_load = NULL;
+ GSList *l;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ win_docs = pluma_window_get_documents (window);
+
+ /* Remove the uris corresponding to documents already open
+ * in "window" and remove duplicates from "uris" list */
+ for (l = files; l != NULL; l = l->next)
+ {
+ if (!is_duplicated_file (files_to_load, l->data))
+ {
+ tab = get_tab_from_file (win_docs, l->data);
+ if (tab != NULL)
+ {
+ if (l == files)
+ {
+ pluma_window_set_active_tab (window, tab);
+ jump_to = FALSE;
+
+ if (line_pos > 0)
+ {
+ PlumaDocument *doc;
+ PlumaView *view;
+
+ doc = pluma_tab_get_document (tab);
+ view = pluma_tab_get_view (tab);
+
+ /* document counts lines starting from 0 */
+ pluma_document_goto_line (doc, line_pos - 1);
+ pluma_view_scroll_to_cursor (view);
+ }
+ }
+
+ ++loaded_files;
+ }
+ else
+ {
+ files_to_load = g_slist_prepend (files_to_load,
+ l->data);
+ }
+ }
+ }
+
+ g_list_free (win_docs);
+
+ if (files_to_load == NULL)
+ return loaded_files;
+
+ files_to_load = g_slist_reverse (files_to_load);
+ l = files_to_load;
+
+ tab = pluma_window_get_active_tab (window);
+ if (tab != NULL)
+ {
+ PlumaDocument *doc;
+
+ doc = pluma_tab_get_document (tab);
+
+ if (pluma_document_is_untouched (doc) &&
+ (pluma_tab_get_state (tab) == PLUMA_TAB_STATE_NORMAL))
+ {
+ gchar *uri;
+
+ // FIXME: pass the GFile to tab when api is there
+ uri = g_file_get_uri (l->data);
+ _pluma_tab_load (tab,
+ uri,
+ encoding,
+ line_pos,
+ create);
+ g_free (uri);
+
+ l = g_slist_next (l);
+ jump_to = FALSE;
+
+ ++loaded_files;
+ }
+ }
+
+ while (l != NULL)
+ {
+ gchar *uri;
+
+ g_return_val_if_fail (l->data != NULL, 0);
+
+ // FIXME: pass the GFile to tab when api is there
+ uri = g_file_get_uri (l->data);
+ tab = pluma_window_create_tab_from_uri (window,
+ uri,
+ encoding,
+ line_pos,
+ create,
+ jump_to);
+ g_free (uri);
+
+ if (tab != NULL)
+ {
+ jump_to = FALSE;
+ ++loaded_files;
+ }
+
+ l = g_slist_next (l);
+ }
+
+ if (loaded_files == 1)
+ {
+ PlumaDocument *doc;
+ gchar *uri_for_display;
+
+ g_return_val_if_fail (tab != NULL, loaded_files);
+
+ doc = pluma_tab_get_document (tab);
+ uri_for_display = pluma_document_get_uri_for_display (doc);
+
+ pluma_statusbar_flash_message (PLUMA_STATUSBAR (window->priv->statusbar),
+ window->priv->generic_message_cid,
+ _("Loading file '%s'\342\200\246"),
+ uri_for_display);
+
+ g_free (uri_for_display);
+ }
+ else
+ {
+ pluma_statusbar_flash_message (PLUMA_STATUSBAR (window->priv->statusbar),
+ window->priv->generic_message_cid,
+ ngettext("Loading %d file\342\200\246",
+ "Loading %d files\342\200\246",
+ loaded_files),
+ loaded_files);
+ }
+
+ /* Free uris_to_load. Note that l points to the first element of uris_to_load */
+ g_slist_free (files_to_load);
+
+ return loaded_files;
+}
+
+
+// FIXME: we should expose API with GFile and just make the uri
+// variants backward compat wrappers
+
+static gint
+load_uri_list (PlumaWindow *window,
+ const GSList *uris,
+ const PlumaEncoding *encoding,
+ gint line_pos,
+ gboolean create)
+{
+ GSList *files = NULL;
+ const GSList *u;
+ gint ret;
+
+ for (u = uris; u != NULL; u = u->next)
+ {
+ gchar *uri = u->data;
+
+ if (pluma_utils_is_valid_uri (uri))
+ files = g_slist_prepend (files, g_file_new_for_uri (uri));
+ else
+ g_warning ("invalid uri: %s", uri);
+ }
+ files = g_slist_reverse (files);
+
+ ret = load_file_list (window, files, encoding, line_pos, create);
+
+ g_slist_foreach (files, (GFunc) g_object_unref, NULL);
+ g_slist_free (files);
+
+ return ret;
+}
+
+/**
+ * pluma_commands_load_uri:
+ *
+ * Do nothing if URI does not exist
+ */
+void
+pluma_commands_load_uri (PlumaWindow *window,
+ const gchar *uri,
+ const PlumaEncoding *encoding,
+ gint line_pos)
+{
+ GSList *uris = NULL;
+
+ g_return_if_fail (PLUMA_IS_WINDOW (window));
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (pluma_utils_is_valid_uri (uri));
+
+ pluma_debug_message (DEBUG_COMMANDS, "Loading URI '%s'", uri);
+
+ uris = g_slist_prepend (uris, (gchar *)uri);
+
+ load_uri_list (window, uris, encoding, line_pos, FALSE);
+
+ g_slist_free (uris);
+}
+
+/**
+ * pluma_commands_load_uris:
+ *
+ * Ignore non-existing URIs
+ */
+gint
+pluma_commands_load_uris (PlumaWindow *window,
+ const GSList *uris,
+ const PlumaEncoding *encoding,
+ gint line_pos)
+{
+ g_return_val_if_fail (PLUMA_IS_WINDOW (window), 0);
+ g_return_val_if_fail ((uris != NULL) && (uris->data != NULL), 0);
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ return load_uri_list (window, uris, encoding, line_pos, FALSE);
+}
+
+/*
+ * This should become public once we convert all api to GFile:
+ */
+static gint
+pluma_commands_load_files (PlumaWindow *window,
+ GSList *files,
+ const PlumaEncoding *encoding,
+ gint line_pos)
+{
+ g_return_val_if_fail (PLUMA_IS_WINDOW (window), 0);
+ g_return_val_if_fail ((files != NULL) && (files->data != NULL), 0);
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ return load_file_list (window, files, encoding, line_pos, FALSE);
+}
+
+/*
+ * From the command line we can specify a line position for the
+ * first doc. Beside specifying a not existing uri creates a
+ * titled document.
+ */
+gint
+_pluma_cmd_load_files_from_prompt (PlumaWindow *window,
+ GSList *files,
+ const PlumaEncoding *encoding,
+ gint line_pos)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+ return load_file_list (window, files, encoding, line_pos, TRUE);
+}
+
+static void
+open_dialog_destroyed (PlumaWindow *window,
+ PlumaFileChooserDialog *dialog)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_OPEN_DIALOG_KEY,
+ NULL);
+}
+
+static void
+open_dialog_response_cb (PlumaFileChooserDialog *dialog,
+ gint response_id,
+ PlumaWindow *window)
+{
+ GSList *files;
+ const PlumaEncoding *encoding;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ if (response_id != GTK_RESPONSE_OK)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ return;
+ }
+
+ files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (dialog));
+ g_return_if_fail (files != NULL);
+
+ encoding = pluma_file_chooser_dialog_get_encoding (dialog);
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ /* Remember the folder we navigated to */
+ _pluma_window_set_default_location (window, files->data);
+
+ pluma_commands_load_files (window,
+ files,
+ encoding,
+ 0);
+
+ g_slist_foreach (files, (GFunc) g_object_unref, NULL);
+ g_slist_free (files);
+}
+
+void
+_pluma_cmd_file_open (GtkAction *action,
+ PlumaWindow *window)
+{
+ GtkWidget *open_dialog;
+ gpointer data;
+ PlumaDocument *doc;
+ GFile *default_path = NULL;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ data = g_object_get_data (G_OBJECT (window), PLUMA_OPEN_DIALOG_KEY);
+
+ if (data != NULL)
+ {
+ g_return_if_fail (PLUMA_IS_FILE_CHOOSER_DIALOG (data));
+
+ gtk_window_present (GTK_WINDOW (data));
+
+ return;
+ }
+
+ /* Translators: "Open Files" is the title of the file chooser window */
+ open_dialog = pluma_file_chooser_dialog_new (_("Open Files"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ NULL,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL);
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_OPEN_DIALOG_KEY,
+ open_dialog);
+
+ g_object_weak_ref (G_OBJECT (open_dialog),
+ (GWeakNotify) open_dialog_destroyed,
+ window);
+
+ /* Set the curret folder uri */
+ doc = pluma_window_get_active_document (window);
+ if (doc != NULL)
+ {
+ GFile *file;
+
+ file = pluma_document_get_location (doc);
+
+ if (file != NULL)
+ {
+ default_path = g_file_get_parent (file);
+ g_object_unref (file);
+ }
+ }
+
+ if (default_path == NULL)
+ default_path = _pluma_window_get_default_location (window);
+
+ if (default_path != NULL)
+ {
+ gchar *uri;
+
+ uri = g_file_get_uri (default_path);
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (open_dialog),
+ uri);
+
+ g_free (uri);
+ g_object_unref (default_path);
+ }
+
+ g_signal_connect (open_dialog,
+ "response",
+ G_CALLBACK (open_dialog_response_cb),
+ window);
+
+ gtk_widget_show (open_dialog);
+}
+
+/* File saving */
+static void file_save_as (PlumaTab *tab, PlumaWindow *window);
+
+static gboolean
+is_read_only (GFile *location)
+{
+ gboolean ret = TRUE; /* default to read only */
+ GFileInfo *info;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ info = g_file_query_info (location,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+
+ if (info != NULL)
+ {
+ if (g_file_info_has_attribute (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
+ {
+ ret = !g_file_info_get_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+ }
+
+ g_object_unref (info);
+ }
+
+ return ret;
+}
+
+/* FIXME: modify this dialog to be similar to the one provided by gtk+ for
+ * already existing files - Paolo (Oct. 11, 2005) */
+static gboolean
+replace_read_only_file (GtkWindow *parent, GFile *file)
+{
+ GtkWidget *dialog;
+ gint ret;
+ gchar *parse_name;
+ gchar *name_for_display;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ parse_name = g_file_get_parse_name (file);
+
+ /* Truncate the name so it doesn't get insanely wide. Note that even
+ * though the dialog uses wrapped text, if the name doesn't contain
+ * white space then the text-wrapping code is too stupid to wrap it.
+ */
+ name_for_display = pluma_utils_str_middle_truncate (parse_name, 50);
+ g_free (parse_name);
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("The file \"%s\" is read-only."),
+ name_for_display);
+ g_free (name_for_display);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("Do you want to try to replace it "
+ "with the one you are saving?"));
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+
+ pluma_dialog_add_button (GTK_DIALOG (dialog),
+ _("_Replace"),
+ GTK_STOCK_SAVE_AS,
+ GTK_RESPONSE_YES);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_CANCEL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ ret = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ return (ret == GTK_RESPONSE_YES);
+}
+
+static void
+save_dialog_response_cb (PlumaFileChooserDialog *dialog,
+ gint response_id,
+ PlumaWindow *window)
+{
+ GFile *file;
+ const PlumaEncoding *encoding;
+ PlumaTab *tab;
+ gpointer data;
+ GSList *tabs_to_save_as;
+ PlumaDocumentNewlineType newline_type;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ tab = PLUMA_TAB (g_object_get_data (G_OBJECT (dialog),
+ PLUMA_TAB_TO_SAVE_AS));
+
+ if (response_id != GTK_RESPONSE_OK)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ goto save_next_tab;
+ }
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+ g_return_if_fail (file != NULL);
+
+ encoding = pluma_file_chooser_dialog_get_encoding (dialog);
+ newline_type = pluma_file_chooser_dialog_get_newline_type (dialog);
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ if (tab != NULL)
+ {
+ PlumaDocument *doc;
+ gchar *parse_name;
+ gchar *uri;
+
+ doc = pluma_tab_get_document (tab);
+ g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
+
+ parse_name = g_file_get_parse_name (file);
+
+ pluma_statusbar_flash_message (PLUMA_STATUSBAR (window->priv->statusbar),
+ window->priv->generic_message_cid,
+ _("Saving file '%s'\342\200\246"),
+ parse_name);
+
+ g_free (parse_name);
+
+ /* let's remember the dir we navigated too,
+ * even if the saving fails... */
+ _pluma_window_set_default_location (window, file);
+
+ // FIXME: pass the GFile to tab when api is there
+ uri = g_file_get_uri (file);
+ _pluma_tab_save_as (tab, uri, encoding, newline_type);
+ g_free (uri);
+ }
+
+ g_object_unref (file);
+
+save_next_tab:
+
+ data = g_object_get_data (G_OBJECT (window),
+ PLUMA_LIST_OF_TABS_TO_SAVE_AS);
+ if (data == NULL)
+ return;
+
+ /* Save As the next tab of the list (we are Saving All files) */
+ tabs_to_save_as = (GSList *)data;
+ g_return_if_fail (tab == PLUMA_TAB (tabs_to_save_as->data));
+
+ /* Remove the first item of the list */
+ tabs_to_save_as = g_slist_delete_link (tabs_to_save_as,
+ tabs_to_save_as);
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_LIST_OF_TABS_TO_SAVE_AS,
+ tabs_to_save_as);
+
+ if (tabs_to_save_as != NULL)
+ {
+ tab = PLUMA_TAB (tabs_to_save_as->data);
+
+ if (GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (tab),
+ PLUMA_IS_CLOSING_TAB)) == TRUE)
+ {
+ g_object_set_data (G_OBJECT (tab),
+ PLUMA_IS_CLOSING_TAB,
+ NULL);
+
+ /* Trace tab state changes */
+ g_signal_connect (tab,
+ "notify::state",
+ G_CALLBACK (tab_state_changed_while_saving),
+ window);
+ }
+
+ pluma_window_set_active_tab (window, tab);
+ file_save_as (tab, window);
+ }
+}
+
+static GtkFileChooserConfirmation
+confirm_overwrite_callback (GtkFileChooser *dialog,
+ gpointer data)
+{
+ gchar *uri;
+ GFile *file;
+ GtkFileChooserConfirmation res;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ uri = gtk_file_chooser_get_uri (dialog);
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ if (is_read_only (file))
+ {
+ if (replace_read_only_file (GTK_WINDOW (dialog), file))
+ res = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
+ else
+ res = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
+ }
+ else
+ {
+ /* fall back to the default confirmation dialog */
+ res = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
+ }
+
+ g_object_unref (file);
+
+ return res;
+}
+
+static void
+file_save_as (PlumaTab *tab,
+ PlumaWindow *window)
+{
+ GtkWidget *save_dialog;
+ GtkWindowGroup *wg;
+ PlumaDocument *doc;
+ GFile *file;
+ gboolean uri_set = FALSE;
+ const PlumaEncoding *encoding;
+ PlumaDocumentNewlineType newline_type;
+
+ g_return_if_fail (PLUMA_IS_TAB (tab));
+ g_return_if_fail (PLUMA_IS_WINDOW (window));
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ save_dialog = pluma_file_chooser_dialog_new (_("Save As\342\200\246"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NULL,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (save_dialog),
+ TRUE);
+ g_signal_connect (save_dialog,
+ "confirm-overwrite",
+ G_CALLBACK (confirm_overwrite_callback),
+ NULL);
+
+ wg = pluma_window_get_group (window);
+
+ gtk_window_group_add_window (wg,
+ GTK_WINDOW (save_dialog));
+
+ /* Save As dialog is modal to its main window */
+ gtk_window_set_modal (GTK_WINDOW (save_dialog), TRUE);
+
+ /* Set the suggested file name */
+ doc = pluma_tab_get_document (tab);
+ file = pluma_document_get_location (doc);
+
+ if (file != NULL)
+ {
+ uri_set = gtk_file_chooser_set_file (GTK_FILE_CHOOSER (save_dialog),
+ file,
+ NULL);
+
+ g_object_unref (file);
+ }
+
+
+ if (!uri_set)
+ {
+ GFile *default_path;
+ gchar *docname;
+
+ default_path = _pluma_window_get_default_location (window);
+ docname = pluma_document_get_short_name_for_display (doc);
+
+ if (default_path != NULL)
+ {
+ gchar *uri;
+
+ uri = g_file_get_uri (default_path);
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog),
+ uri);
+
+ g_free (uri);
+ g_object_unref (default_path);
+ }
+
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
+ docname);
+
+ g_free (docname);
+ }
+
+ /* Set suggested encoding */
+ encoding = pluma_document_get_encoding (doc);
+ g_return_if_fail (encoding != NULL);
+
+ newline_type = pluma_document_get_newline_type (doc);
+
+ pluma_file_chooser_dialog_set_encoding (PLUMA_FILE_CHOOSER_DIALOG (save_dialog),
+ encoding);
+
+ pluma_file_chooser_dialog_set_newline_type (PLUMA_FILE_CHOOSER_DIALOG (save_dialog),
+ newline_type);
+
+ g_object_set_data (G_OBJECT (save_dialog),
+ PLUMA_TAB_TO_SAVE_AS,
+ tab);
+
+ g_signal_connect (save_dialog,
+ "response",
+ G_CALLBACK (save_dialog_response_cb),
+ window);
+
+ gtk_widget_show (save_dialog);
+}
+
+static void
+file_save (PlumaTab *tab,
+ PlumaWindow *window)
+{
+ PlumaDocument *doc;
+ gchar *uri_for_display;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_if_fail (PLUMA_IS_TAB (tab));
+ g_return_if_fail (PLUMA_IS_WINDOW (window));
+
+ doc = pluma_tab_get_document (tab);
+ g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
+
+ if (pluma_document_is_untitled (doc) ||
+ pluma_document_get_readonly (doc))
+ {
+ pluma_debug_message (DEBUG_COMMANDS, "Untitled or Readonly");
+
+ file_save_as (tab, window);
+
+ return;
+ }
+
+ uri_for_display = pluma_document_get_uri_for_display (doc);
+ pluma_statusbar_flash_message (PLUMA_STATUSBAR (window->priv->statusbar),
+ window->priv->generic_message_cid,
+ _("Saving file '%s'\342\200\246"),
+ uri_for_display);
+
+ g_free (uri_for_display);
+
+ _pluma_tab_save (tab);
+}
+
+void
+_pluma_cmd_file_save (GtkAction *action,
+ PlumaWindow *window)
+{
+ PlumaTab *tab;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ tab = pluma_window_get_active_tab (window);
+ if (tab == NULL)
+ return;
+
+ file_save (tab, window);
+}
+
+void
+_pluma_cmd_file_save_as (GtkAction *action,
+ PlumaWindow *window)
+{
+ PlumaTab *tab;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ tab = pluma_window_get_active_tab (window);
+ if (tab == NULL)
+ return;
+
+ file_save_as (tab, window);
+}
+
+static gboolean
+document_needs_saving (PlumaDocument *doc)
+{
+ if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)))
+ return TRUE;
+
+ /* we check if it was deleted only for local files
+ * since for remote files it may hang */
+ if (pluma_document_is_local (doc) && pluma_document_get_deleted (doc))
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * The docs in the list must belong to the same PlumaWindow.
+ */
+void
+_pluma_cmd_file_save_documents_list (PlumaWindow *window,
+ GList *docs)
+{
+ GList *l;
+ GSList *tabs_to_save_as = NULL;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_if_fail (!(pluma_window_get_state (window) &
+ (PLUMA_WINDOW_STATE_PRINTING |
+ PLUMA_WINDOW_STATE_SAVING_SESSION)));
+
+ l = docs;
+ while (l != NULL)
+ {
+ PlumaDocument *doc;
+ PlumaTab *t;
+ PlumaTabState state;
+
+ g_return_if_fail (PLUMA_IS_DOCUMENT (l->data));
+
+ doc = PLUMA_DOCUMENT (l->data);
+ t = pluma_tab_get_from_document (doc);
+ state = pluma_tab_get_state (t);
+
+ g_return_if_fail (state != PLUMA_TAB_STATE_PRINTING);
+ g_return_if_fail (state != PLUMA_TAB_STATE_PRINT_PREVIEWING);
+ g_return_if_fail (state != PLUMA_TAB_STATE_CLOSING);
+
+ if ((state == PLUMA_TAB_STATE_NORMAL) ||
+ (state == PLUMA_TAB_STATE_SHOWING_PRINT_PREVIEW) ||
+ (state == PLUMA_TAB_STATE_GENERIC_NOT_EDITABLE))
+ {
+ /* FIXME: manage the case of local readonly files owned by the
+ user is running pluma - Paolo (Dec. 8, 2005) */
+ if (pluma_document_is_untitled (doc) ||
+ pluma_document_get_readonly (doc))
+ {
+ if (document_needs_saving (doc))
+ {
+ tabs_to_save_as = g_slist_prepend (tabs_to_save_as,
+ t);
+ }
+ }
+ else
+ {
+ file_save (t, window);
+ }
+ }
+ else
+ {
+ /* If the state is:
+ - PLUMA_TAB_STATE_LOADING: we do not save since we are sure the file is unmodified
+ - PLUMA_TAB_STATE_REVERTING: we do not save since the user wants
+ to return back to the version of the file she previously saved
+ - PLUMA_TAB_STATE_SAVING: well, we are already saving (no need to save again)
+ - PLUMA_TAB_STATE_PRINTING, PLUMA_TAB_STATE_PRINT_PREVIEWING: there is not a
+ real reason for not saving in this case, we do not save to avoid to run
+ two operations using the message area at the same time (may be we can remove
+ this limitation in the future). Note that SaveAll, ClosAll
+ and Quit are unsensitive if the window state is PRINTING.
+ - PLUMA_TAB_STATE_GENERIC_ERROR: we do not save since the document contains
+ errors (I don't think this is a very frequent case, we should probably remove
+ this state)
+ - PLUMA_TAB_STATE_LOADING_ERROR: there is nothing to save
+ - PLUMA_TAB_STATE_REVERTING_ERROR: there is nothing to save and saving the current
+ document will overwrite the copy of the file the user wants to go back to
+ - PLUMA_TAB_STATE_SAVING_ERROR: we do not save since we just failed to save, so there is
+ no reason to automatically retry... we wait for user intervention
+ - PLUMA_TAB_STATE_CLOSING: this state is invalid in this case
+ */
+
+ gchar *uri_for_display;
+
+ uri_for_display = pluma_document_get_uri_for_display (doc);
+ pluma_debug_message (DEBUG_COMMANDS,
+ "File '%s' not saved. State: %d",
+ uri_for_display,
+ state);
+ g_free (uri_for_display);
+ }
+
+ l = g_list_next (l);
+ }
+
+ if (tabs_to_save_as != NULL)
+ {
+ PlumaTab *tab;
+
+ tabs_to_save_as = g_slist_reverse (tabs_to_save_as );
+
+ g_return_if_fail (g_object_get_data (G_OBJECT (window),
+ PLUMA_LIST_OF_TABS_TO_SAVE_AS) == NULL);
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_LIST_OF_TABS_TO_SAVE_AS,
+ tabs_to_save_as);
+
+ tab = PLUMA_TAB (tabs_to_save_as->data);
+
+ pluma_window_set_active_tab (window, tab);
+ file_save_as (tab, window);
+ }
+}
+
+void
+pluma_commands_save_all_documents (PlumaWindow *window)
+{
+ GList *docs;
+
+ g_return_if_fail (PLUMA_IS_WINDOW (window));
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ docs = pluma_window_get_documents (window);
+
+ _pluma_cmd_file_save_documents_list (window, docs);
+
+ g_list_free (docs);
+}
+
+void
+_pluma_cmd_file_save_all (GtkAction *action,
+ PlumaWindow *window)
+{
+ pluma_commands_save_all_documents (window);
+}
+
+void
+pluma_commands_save_document (PlumaWindow *window,
+ PlumaDocument *document)
+{
+ PlumaTab *tab;
+
+ g_return_if_fail (PLUMA_IS_WINDOW (window));
+ g_return_if_fail (PLUMA_IS_DOCUMENT (document));
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ tab = pluma_tab_get_from_document (document);
+ file_save (tab, window);
+}
+
+/* File revert */
+static void
+do_revert (PlumaWindow *window,
+ PlumaTab *tab)
+{
+ PlumaDocument *doc;
+ gchar *docname;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ doc = pluma_tab_get_document (tab);
+ docname = pluma_document_get_short_name_for_display (doc);
+
+ pluma_statusbar_flash_message (PLUMA_STATUSBAR (window->priv->statusbar),
+ window->priv->generic_message_cid,
+ _("Reverting the document '%s'\342\200\246"),
+ docname);
+
+ g_free (docname);
+
+ _pluma_tab_revert (tab);
+}
+
+static void
+revert_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ PlumaWindow *window)
+{
+ PlumaTab *tab;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ /* FIXME: we are relying on the fact that the dialog is
+ modal so the active tab can't be changed...
+ not very nice - Paolo (Oct 11, 2005) */
+ tab = pluma_window_get_active_tab (window);
+ if (tab == NULL)
+ return;
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ do_revert (window, tab);
+ }
+}
+
+static GtkWidget *
+revert_dialog (PlumaWindow *window,
+ PlumaDocument *doc)
+{
+ GtkWidget *dialog;
+ gchar *docname;
+ gchar *primary_msg;
+ gchar *secondary_msg;
+ glong seconds;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ docname = pluma_document_get_short_name_for_display (doc);
+ primary_msg = g_strdup_printf (_("Revert unsaved changes to document '%s'?"),
+ docname);
+ g_free (docname);
+
+ seconds = MAX (1, _pluma_document_get_seconds_since_last_save_or_load (doc));
+
+ if (seconds < 55)
+ {
+ secondary_msg = g_strdup_printf (
+ ngettext ("Changes made to the document in the last %ld second "
+ "will be permanently lost.",
+ "Changes made to the document in the last %ld seconds "
+ "will be permanently lost.",
+ seconds),
+ seconds);
+ }
+ else if (seconds < 75) /* 55 <= seconds < 75 */
+ {
+ secondary_msg = g_strdup (_("Changes made to the document in the last minute "
+ "will be permanently lost."));
+ }
+ else if (seconds < 110) /* 75 <= seconds < 110 */
+ {
+ secondary_msg = g_strdup_printf (
+ ngettext ("Changes made to the document in the last minute and "
+ "%ld second will be permanently lost.",
+ "Changes made to the document in the last minute and "
+ "%ld seconds will be permanently lost.",
+ seconds - 60 ),
+ seconds - 60);
+ }
+ else if (seconds < 3600)
+ {
+ secondary_msg = g_strdup_printf (
+ ngettext ("Changes made to the document in the last %ld minute "
+ "will be permanently lost.",
+ "Changes made to the document in the last %ld minutes "
+ "will be permanently lost.",
+ seconds / 60),
+ seconds / 60);
+ }
+ else if (seconds < 7200)
+ {
+ gint minutes;
+ seconds -= 3600;
+
+ minutes = seconds / 60;
+ if (minutes < 5)
+ {
+ secondary_msg = g_strdup (_("Changes made to the document in the last hour "
+ "will be permanently lost."));
+ }
+ else
+ {
+ secondary_msg = g_strdup_printf (
+ ngettext ("Changes made to the document in the last hour and "
+ "%d minute will be permanently lost.",
+ "Changes made to the document in the last hour and "
+ "%d minutes will be permanently lost.",
+ minutes),
+ minutes);
+ }
+ }
+ else
+ {
+ gint hours;
+
+ hours = seconds / 3600;
+
+ secondary_msg = g_strdup_printf (
+ ngettext ("Changes made to the document in the last %d hour "
+ "will be permanently lost.",
+ "Changes made to the document in the last %d hours "
+ "will be permanently lost.",
+ hours),
+ hours);
+ }
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ "%s", primary_msg);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", secondary_msg);
+ g_free (primary_msg);
+ g_free (secondary_msg);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+
+ pluma_dialog_add_button (GTK_DIALOG (dialog),
+ _("_Revert"),
+ GTK_STOCK_REVERT_TO_SAVED,
+ GTK_RESPONSE_OK);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_CANCEL);
+
+ return dialog;
+}
+
+void
+_pluma_cmd_file_revert (GtkAction *action,
+ PlumaWindow *window)
+{
+ PlumaTab *tab;
+ PlumaDocument *doc;
+ GtkWidget *dialog;
+ GtkWindowGroup *wg;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ tab = pluma_window_get_active_tab (window);
+ g_return_if_fail (tab != NULL);
+
+ /* If we are already displaying a notification
+ * reverting will drop local modifications, do
+ * not bug the user further */
+ if (pluma_tab_get_state (tab) == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)
+ {
+ do_revert (window, tab);
+ return;
+ }
+
+ doc = pluma_tab_get_document (tab);
+ g_return_if_fail (doc != NULL);
+ g_return_if_fail (!pluma_document_is_untitled (doc));
+
+ dialog = revert_dialog (window, doc);
+
+ wg = pluma_window_get_group (window);
+
+ gtk_window_group_add_window (wg, GTK_WINDOW (dialog));
+
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (revert_dialog_response_cb),
+ window);
+
+ gtk_widget_show (dialog);
+}
+
+/* Close tab */
+static gboolean
+really_close_tab (PlumaTab *tab)
+{
+ GtkWidget *toplevel;
+ PlumaWindow *window;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_val_if_fail (pluma_tab_get_state (tab) == PLUMA_TAB_STATE_CLOSING,
+ FALSE);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tab));
+ g_return_val_if_fail (PLUMA_IS_WINDOW (toplevel), FALSE);
+
+ window = PLUMA_WINDOW (toplevel);
+
+ pluma_window_close_tab (window, tab);
+
+ if (pluma_window_get_active_tab (window) == NULL)
+ {
+ gboolean is_quitting;
+
+ is_quitting = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING));
+
+ if (is_quitting)
+ gtk_widget_destroy (GTK_WIDGET (window));
+ }
+
+ return FALSE;
+}
+
+static void
+tab_state_changed_while_saving (PlumaTab *tab,
+ GParamSpec *pspec,
+ PlumaWindow *window)
+{
+ PlumaTabState ts;
+
+ ts = pluma_tab_get_state (tab);
+
+ pluma_debug_message (DEBUG_COMMANDS, "State while saving: %d\n", ts);
+
+ /* When the state become NORMAL, it means the saving operation is
+ finished */
+ if (ts == PLUMA_TAB_STATE_NORMAL)
+ {
+ PlumaDocument *doc;
+
+ g_signal_handlers_disconnect_by_func (tab,
+ G_CALLBACK (tab_state_changed_while_saving),
+ window);
+
+ doc = pluma_tab_get_document (tab);
+ g_return_if_fail (doc != NULL);
+
+ /* If the saving operation failed or was interrupted, then the
+ document is still "modified" -> do not close the tab */
+ if (document_needs_saving (doc))
+ return;
+
+ /* Close the document only if it has been succesfully saved.
+ Tab state is set to CLOSING (it is a state without exiting
+ transitions) and the tab is closed in a idle handler */
+ _pluma_tab_mark_for_closing (tab);
+
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+ (GSourceFunc)really_close_tab,
+ tab,
+ NULL);
+ }
+}
+
+static void
+save_and_close (PlumaTab *tab,
+ PlumaWindow *window)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+ /* Trace tab state changes */
+ g_signal_connect (tab,
+ "notify::state",
+ G_CALLBACK (tab_state_changed_while_saving),
+ window);
+
+ file_save (tab, window);
+}
+
+static void
+save_as_and_close (PlumaTab *tab,
+ PlumaWindow *window)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_object_set_data (G_OBJECT (tab),
+ PLUMA_IS_CLOSING_TAB,
+ NULL);
+
+ /* Trace tab state changes */
+ g_signal_connect (tab,
+ "notify::state",
+ G_CALLBACK (tab_state_changed_while_saving),
+ window);
+
+ pluma_window_set_active_tab (window, tab);
+ file_save_as (tab, window);
+}
+
+static void
+save_and_close_all_documents (const GList *docs,
+ PlumaWindow *window)
+{
+ GList *tabs;
+ GList *l;
+ GSList *sl;
+ GSList *tabs_to_save_as;
+ GSList *tabs_to_save_and_close;
+ GList *tabs_to_close;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_if_fail (!(pluma_window_get_state (window) & PLUMA_WINDOW_STATE_PRINTING));
+
+ tabs = gtk_container_get_children (
+ GTK_CONTAINER (_pluma_window_get_notebook (window)));
+
+ tabs_to_save_as = NULL;
+ tabs_to_save_and_close = NULL;
+ tabs_to_close = NULL;
+
+ l = tabs;
+ while (l != NULL)
+ {
+ PlumaTab *t;
+ PlumaTabState state;
+ PlumaDocument *doc;
+
+ t = PLUMA_TAB (l->data);
+
+ state = pluma_tab_get_state (t);
+ doc = pluma_tab_get_document (t);
+
+ /* If the state is: ([*] invalid states)
+ - PLUMA_TAB_STATE_NORMAL: close (and if needed save)
+ - PLUMA_TAB_STATE_LOADING: close, we are sure the file is unmodified
+ - PLUMA_TAB_STATE_REVERTING: since the user wants
+ to return back to the version of the file she previously saved, we can close
+ without saving (CHECK: are we sure this is the right behavior, suppose the case
+ the original file has been deleted)
+ - [*] PLUMA_TAB_STATE_SAVING: invalid, ClosAll
+ and Quit are unsensitive if the window state is SAVING.
+ - [*] PLUMA_TAB_STATE_PRINTING, PLUMA_TAB_STATE_PRINT_PREVIEWING: there is not a
+ real reason for not closing in this case, we do not save to avoid to run
+ two operations using the message area at the same time (may be we can remove
+ this limitation in the future). Note that ClosAll
+ and Quit are unsensitive if the window state is PRINTING.
+ - PLUMA_TAB_STATE_SHOWING_PRINT_PREVIEW: close (and if needed save)
+ - PLUMA_TAB_STATE_LOADING_ERROR: close without saving (if the state is LOADING_ERROR then the
+ document is not modified)
+ - PLUMA_TAB_STATE_REVERTING_ERROR: we do not close since the document contains errors
+ - PLUMA_TAB_STATE_SAVING_ERROR: we do not close since the document contains errors
+ - PLUMA_TAB_STATE_GENERIC_ERROR: we do not close since the document contains
+ errors (CHECK: we should problably remove this state)
+ - [*] PLUMA_TAB_STATE_CLOSING: this state is invalid in this case
+ */
+
+ g_return_if_fail (state != PLUMA_TAB_STATE_PRINTING);
+ g_return_if_fail (state != PLUMA_TAB_STATE_PRINT_PREVIEWING);
+ g_return_if_fail (state != PLUMA_TAB_STATE_CLOSING);
+ g_return_if_fail (state != PLUMA_TAB_STATE_SAVING);
+
+ if ((state != PLUMA_TAB_STATE_SAVING_ERROR) &&
+ (state != PLUMA_TAB_STATE_GENERIC_ERROR) &&
+ (state != PLUMA_TAB_STATE_REVERTING_ERROR))
+ {
+ if ((g_list_index ((GList *)docs, doc) >= 0) &&
+ (state != PLUMA_TAB_STATE_LOADING) &&
+ (state != PLUMA_TAB_STATE_LOADING_ERROR) &&
+ (state != PLUMA_TAB_STATE_REVERTING)) /* CHECK: is this the right behavior with REVERTING ?*/
+ {
+ /* The document must be saved before closing */
+ g_return_if_fail (document_needs_saving (doc));
+
+ /* FIXME: manage the case of local readonly files owned by the
+ user is running pluma - Paolo (Dec. 8, 2005) */
+ if (pluma_document_is_untitled (doc) ||
+ pluma_document_get_readonly (doc))
+ {
+ g_object_set_data (G_OBJECT (t),
+ PLUMA_IS_CLOSING_TAB,
+ GBOOLEAN_TO_POINTER (TRUE));
+
+ tabs_to_save_as = g_slist_prepend (tabs_to_save_as,
+ t);
+ }
+ else
+ {
+ tabs_to_save_and_close = g_slist_prepend (tabs_to_save_and_close,
+ t);
+ }
+ }
+ else
+ {
+ /* The document must be closed without saving */
+ tabs_to_close = g_list_prepend (tabs_to_close,
+ t);
+ }
+ }
+
+ l = g_list_next (l);
+ }
+
+ g_list_free (tabs);
+
+ /* Close all tabs to close (in a sync way) */
+ pluma_window_close_tabs (window, tabs_to_close);
+ g_list_free (tabs_to_close);
+
+ /* Save and close all the files in tabs_to_save_and_close */
+ sl = tabs_to_save_and_close;
+ while (sl != NULL)
+ {
+ save_and_close (PLUMA_TAB (sl->data),
+ window);
+ sl = g_slist_next (sl);
+ }
+ g_slist_free (tabs_to_save_and_close);
+
+ /* Save As and close all the files in tabs_to_save_as */
+ if (tabs_to_save_as != NULL)
+ {
+ PlumaTab *tab;
+
+ tabs_to_save_as = g_slist_reverse (tabs_to_save_as );
+
+ g_return_if_fail (g_object_get_data (G_OBJECT (window),
+ PLUMA_LIST_OF_TABS_TO_SAVE_AS) == NULL);
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_LIST_OF_TABS_TO_SAVE_AS,
+ tabs_to_save_as);
+
+ tab = PLUMA_TAB (tabs_to_save_as->data);
+
+ save_as_and_close (tab, window);
+ }
+}
+
+static void
+save_and_close_document (const GList *docs,
+ PlumaWindow *window)
+{
+ PlumaTab *tab;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_if_fail (docs->next == NULL);
+
+ tab = pluma_tab_get_from_document (PLUMA_DOCUMENT (docs->data));
+ g_return_if_fail (tab != NULL);
+
+ save_and_close (tab, window);
+}
+
+static void
+close_all_tabs (PlumaWindow *window)
+{
+ gboolean is_quitting;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ /* There is no document to save -> close all tabs */
+ pluma_window_close_all_tabs (window);
+
+ is_quitting = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING));
+
+ if (is_quitting)
+ gtk_widget_destroy (GTK_WIDGET (window));
+
+ return;
+}
+
+static void
+close_document (PlumaWindow *window,
+ PlumaDocument *doc)
+{
+ PlumaTab *tab;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ tab = pluma_tab_get_from_document (doc);
+ g_return_if_fail (tab != NULL);
+
+ pluma_window_close_tab (window, tab);
+}
+
+static void
+close_confirmation_dialog_response_handler (PlumaCloseConfirmationDialog *dlg,
+ gint response_id,
+ PlumaWindow *window)
+{
+ GList *selected_documents;
+ gboolean is_closing_all;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ is_closing_all = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window),
+ PLUMA_IS_CLOSING_ALL));
+
+ gtk_widget_hide (GTK_WIDGET (dlg));
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_YES: /* Save and Close */
+ selected_documents = pluma_close_confirmation_dialog_get_selected_documents (dlg);
+ if (selected_documents == NULL)
+ {
+ if (is_closing_all)
+ {
+ /* There is no document to save -> close all tabs */
+ /* We call gtk_widget_destroy before close_all_tabs
+ * because close_all_tabs could destroy the pluma window */
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+
+ close_all_tabs (window);
+
+ return;
+ }
+ else
+ g_return_if_reached ();
+ }
+ else
+ {
+ if (is_closing_all)
+ {
+ save_and_close_all_documents (selected_documents,
+ window);
+ }
+ else
+ {
+ save_and_close_document (selected_documents,
+ window);
+ }
+ }
+
+ g_list_free (selected_documents);
+
+ break;
+
+ case GTK_RESPONSE_NO: /* Close without Saving */
+ if (is_closing_all)
+ {
+ /* We call gtk_widget_destroy before close_all_tabs
+ * because close_all_tabs could destroy the pluma window */
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+
+ close_all_tabs (window);
+
+ return;
+ }
+ else
+ {
+ const GList *unsaved_documents;
+
+ unsaved_documents = pluma_close_confirmation_dialog_get_unsaved_documents (dlg);
+ g_return_if_fail (unsaved_documents->next == NULL);
+
+ close_document (window,
+ PLUMA_DOCUMENT (unsaved_documents->data));
+ }
+
+ break;
+ default: /* Do not close */
+
+ /* Reset is_quitting flag */
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING,
+ GBOOLEAN_TO_POINTER (FALSE));
+
+#ifdef OS_OSX
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING_ALL,
+ GINT_TO_POINTER (FALSE));
+#endif
+ break;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+}
+
+/* Returns TRUE if the tab can be immediately closed */
+static gboolean
+tab_can_close (PlumaTab *tab,
+ GtkWindow *window)
+{
+ PlumaDocument *doc;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ doc = pluma_tab_get_document (tab);
+
+ if (!_pluma_tab_can_close (tab))
+ {
+ GtkWidget *dlg;
+
+ dlg = pluma_close_confirmation_dialog_new_single (
+ window,
+ doc,
+ FALSE);
+
+ g_signal_connect (dlg,
+ "response",
+ G_CALLBACK (close_confirmation_dialog_response_handler),
+ window);
+
+ gtk_widget_show (dlg);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* CHECK: we probably need this one public for plugins...
+ * maybe even a _list variant. Or maybe it's better make
+ * pluma_window_close_tab always run the confirm dialog?
+ * we should not allow closing a tab without resetting the
+ * PLUMA_IS_CLOSING_ALL flag!
+ */
+void
+_pluma_cmd_file_close_tab (PlumaTab *tab,
+ PlumaWindow *window)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_if_fail (GTK_WIDGET (window) == gtk_widget_get_toplevel (GTK_WIDGET (tab)));
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_CLOSING_ALL,
+ GBOOLEAN_TO_POINTER (FALSE));
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING,
+ GBOOLEAN_TO_POINTER (FALSE));
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING_ALL,
+ GINT_TO_POINTER (FALSE));
+
+
+ if (tab_can_close (tab, GTK_WINDOW (window)))
+ pluma_window_close_tab (window, tab);
+}
+
+void
+_pluma_cmd_file_close (GtkAction *action,
+ PlumaWindow *window)
+{
+ PlumaTab *active_tab;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ active_tab = pluma_window_get_active_tab (window);
+
+ if (active_tab == NULL)
+ {
+#ifdef OS_OSX
+ /* Close the window on OS X */
+ gtk_widget_destroy (GTK_WIDGET (window));
+#endif
+ return;
+ }
+
+ _pluma_cmd_file_close_tab (active_tab, window);
+}
+
+/* Close all tabs */
+static void
+file_close_all (PlumaWindow *window,
+ gboolean is_quitting)
+{
+ GList *unsaved_docs;
+ GtkWidget *dlg;
+
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_if_fail (!(pluma_window_get_state (window) &
+ (PLUMA_WINDOW_STATE_SAVING |
+ PLUMA_WINDOW_STATE_PRINTING |
+ PLUMA_WINDOW_STATE_SAVING_SESSION)));
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_CLOSING_ALL,
+ GBOOLEAN_TO_POINTER (TRUE));
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING,
+ GBOOLEAN_TO_POINTER (is_quitting));
+
+ unsaved_docs = pluma_window_get_unsaved_documents (window);
+
+ if (unsaved_docs == NULL)
+ {
+ /* There is no document to save -> close all tabs */
+ pluma_window_close_all_tabs (window);
+
+ if (is_quitting)
+ gtk_widget_destroy (GTK_WIDGET (window));
+
+ return;
+ }
+
+ if (unsaved_docs->next == NULL)
+ {
+ /* There is only one unsaved document */
+ PlumaTab *tab;
+ PlumaDocument *doc;
+
+ doc = PLUMA_DOCUMENT (unsaved_docs->data);
+
+ tab = pluma_tab_get_from_document (doc);
+ g_return_if_fail (tab != NULL);
+
+ pluma_window_set_active_tab (window, tab);
+
+ dlg = pluma_close_confirmation_dialog_new_single (
+ GTK_WINDOW (window),
+ doc,
+ FALSE);
+ }
+ else
+ {
+ dlg = pluma_close_confirmation_dialog_new (GTK_WINDOW (window),
+ unsaved_docs,
+ FALSE);
+ }
+
+ g_list_free (unsaved_docs);
+
+ g_signal_connect (dlg,
+ "response",
+ G_CALLBACK (close_confirmation_dialog_response_handler),
+ window);
+
+ gtk_widget_show (dlg);
+}
+
+void
+_pluma_cmd_file_close_all (GtkAction *action,
+ PlumaWindow *window)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+ g_return_if_fail (!(pluma_window_get_state (window) &
+ (PLUMA_WINDOW_STATE_SAVING |
+ PLUMA_WINDOW_STATE_PRINTING |
+ PLUMA_WINDOW_STATE_SAVING_SESSION)));
+
+ file_close_all (window, FALSE);
+}
+
+/* Quit */
+#ifdef OS_OSX
+static void
+quit_all ()
+{
+ GList *windows;
+ GList *item;
+ PlumaApp *app;
+
+ app = pluma_app_get_default ();
+ windows = g_list_copy ((GList *)pluma_app_get_windows (app));
+
+ for (item = windows; item; item = g_list_next (item))
+ {
+ PlumaWindow *window = PLUMA_WINDOW (item->data);
+
+ g_object_set_data (G_OBJECT (window),
+ PLUMA_IS_QUITTING_ALL,
+ GINT_TO_POINTER (TRUE));
+
+ if (!(pluma_window_get_state (window) &
+ (PLUMA_WINDOW_STATE_SAVING |
+ PLUMA_WINDOW_STATE_PRINTING |
+ PLUMA_WINDOW_STATE_SAVING_SESSION)))
+ {
+ file_close_all (window, TRUE);
+ }
+ }
+
+ g_list_free (windows);
+}
+#endif
+
+void
+_pluma_cmd_file_quit (GtkAction *action,
+ PlumaWindow *window)
+{
+ pluma_debug (DEBUG_COMMANDS);
+
+#ifdef OS_OSX
+ if (action != NULL)
+ {
+ quit_all ();
+ return;
+ }
+#endif
+
+ g_return_if_fail (!(pluma_window_get_state (window) &
+ (PLUMA_WINDOW_STATE_SAVING |
+ PLUMA_WINDOW_STATE_PRINTING |
+ PLUMA_WINDOW_STATE_SAVING_SESSION)));
+
+ file_close_all (window, TRUE);
+}