/*
 * gedit-commands-file.c
 * This file is part of gedit
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

#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 "gedit-commands.h"
#include "gedit-window.h"
#include "gedit-window-private.h"
#include "gedit-notebook.h"
#include "gedit-document.h"
#include "gedit-statusbar.h"
#include "gedit-debug.h"
#include "gedit-utils.h"
#include "gedit-file-chooser-dialog.h"
#include "gedit-close-confirmation-dialog.h"

#define GEDIT_OPEN_DIALOG_KEY "gedit-open-dialog-key"
#define GEDIT_TAB_TO_SAVE_AS "gedit-tab-to-save-as"
#define GEDIT_LIST_OF_TABS_TO_SAVE_AS "gedit-list-of-tabs-to-save-as"
#define GEDIT_IS_CLOSING_ALL "gedit-is-closing-all"
#define GEDIT_NOTEBOOK_TO_CLOSE "gedit-notebook-to-close"
#define GEDIT_IS_QUITTING "gedit-is-quitting"
#define GEDIT_IS_CLOSING_TAB "gedit-is-closing-tab"
#define GEDIT_IS_QUITTING_ALL "gedit-is-quitting-all"

static void tab_state_changed_while_saving (GeditTab    *tab,
					    GParamSpec  *pspec,
					    GeditWindow *window);

void
_gedit_cmd_file_new (GSimpleAction *action,
                     GVariant      *parameter,
                     gpointer       user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);

	gedit_debug (DEBUG_COMMANDS);

	gedit_window_create_tab (window, TRUE);
}

static GeditTab *
get_tab_from_file (GList *docs,
		   GFile *file)
{
	GeditTab *tab = NULL;

	while (docs != NULL)
	{
		GeditDocument *d;
		GFile *l;

		d = GEDIT_DOCUMENT (docs->data);

		l = gedit_document_get_location (d);
		if (l != NULL)
		{
			if (g_file_equal (l, file))
			{
				tab = gedit_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 GSList *
load_file_list (GeditWindow         *window,
		const GSList        *files,
		const GeditEncoding *encoding,
		gint                 line_pos,
		gint                 column_pos,
		gboolean             create)
{
	GeditTab *tab;
	GSList *loaded_files = NULL; /* Number of files to load */
	gboolean jump_to = TRUE; /* Whether to jump to the new tab */
	GList *win_docs;
	GSList *files_to_load = NULL;
	const GSList *l;
	gint num_loaded_files = 0;

	gedit_debug (DEBUG_COMMANDS);

	win_docs = gedit_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))
		{
			continue;
		}

		tab = get_tab_from_file (win_docs, l->data);
		if (tab != NULL)
		{
			if (l == files)
			{
				GeditDocument *doc;

				gedit_window_set_active_tab (window, tab);
				jump_to = FALSE;
				doc = gedit_tab_get_document (tab);

				if (line_pos > 0)
				{
					if (column_pos > 0)
					{
						gedit_document_goto_line_offset (doc,
						                                 line_pos - 1,
						                                 column_pos - 1);
					}
					else
					{
						gedit_document_goto_line (doc, line_pos - 1);
					}

					gedit_view_scroll_to_cursor (gedit_tab_get_view (tab));
				}
			}

			++num_loaded_files;
			loaded_files = g_slist_prepend (loaded_files,
			                                gedit_tab_get_document (tab));
		}
		else
		{
			files_to_load = g_slist_prepend (files_to_load,
							 l->data);
		}
	}

	g_list_free (win_docs);

	if (files_to_load == NULL)
	{
		return g_slist_reverse (loaded_files);
	}

	files_to_load = g_slist_reverse (files_to_load);
	l = files_to_load;

	tab = gedit_window_get_active_tab (window);
	if (tab != NULL)
	{
		GeditDocument *doc;

		doc = gedit_tab_get_document (tab);

		if (gedit_document_is_untouched (doc) &&
		    (gedit_tab_get_state (tab) == GEDIT_TAB_STATE_NORMAL))
		{
			_gedit_tab_load (tab,
					 l->data,
					 encoding,
					 line_pos,
					 column_pos,
					 create);

			/* make sure the view has focus */
			gtk_widget_grab_focus (GTK_WIDGET (gedit_tab_get_view (tab)));

			l = g_slist_next (l);
			jump_to = FALSE;

			++num_loaded_files;
			loaded_files = g_slist_prepend (loaded_files,
			                                gedit_tab_get_document (tab));
		}
	}

	while (l != NULL)
	{
		g_return_val_if_fail (l->data != NULL, 0);

		tab = gedit_window_create_tab_from_location (window,
							     l->data,
							     encoding,
							     line_pos,
							     column_pos,
							     create,
							     jump_to);

		if (tab != NULL)
		{
			jump_to = FALSE;

			++num_loaded_files;
			loaded_files = g_slist_prepend (loaded_files,
			                                gedit_tab_get_document (tab));
		}

		l = g_slist_next (l);
	}

	loaded_files = g_slist_reverse (loaded_files);

	if (num_loaded_files == 1)
	{
		GeditDocument *doc;
		gchar *uri_for_display;

		g_return_val_if_fail (tab != NULL, loaded_files);

		doc = gedit_tab_get_document (tab);
		uri_for_display = gedit_document_get_uri_for_display (doc);

		gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar),
					       window->priv->generic_message_cid,
					       _("Loading file '%s'\342\200\246"),
					       uri_for_display);

		g_free (uri_for_display);
	}
	else
	{
		gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar),
					       window->priv->generic_message_cid,
					       ngettext("Loading %d file\342\200\246",
							"Loading %d files\342\200\246",
							num_loaded_files),
					       num_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;
}

/**
 * gedit_commands_load_location:
 * @window: a #GeditWindow
 * @location: a #GFile to be loaded
 * @encoding: (allow-none): the #GeditEncoding of @location
 * @line_pos: the line position to place the cursor when @location is loaded
 * @column_pos: the line column to place the cursor when @location is loaded
 *
 * Loads @location. Ignores non-existing locations
 */
void
gedit_commands_load_location (GeditWindow         *window,
			      GFile               *location,
			      const GeditEncoding *encoding,
			      gint                 line_pos,
			      gint                 column_pos)
{
	GSList *locations = NULL;
	gchar *uri;
	GSList *ret;

	g_return_if_fail (GEDIT_IS_WINDOW (window));
	g_return_if_fail (G_IS_FILE (location));
	g_return_if_fail (gedit_utils_is_valid_location (location));

	uri = g_file_get_uri (location);
	gedit_debug_message (DEBUG_COMMANDS, "Loading URI '%s'", uri);
	g_free (uri);

	locations = g_slist_prepend (locations, location);

	ret = load_file_list (window, locations, encoding, line_pos, column_pos, FALSE);
	g_slist_free (ret);

	g_slist_free (locations);
}

/**
 * gedit_commands_load_locations:
 * @window: a #GeditWindow
 * @locations: (element-type Gio.File): the locations to be loaded
 * @encoding: (allow-none): the #GeditEncoding of @location
 * @line_pos: the line position to place the cursor when @location is loaded
 * @column_pos: the line column to place the cursor when @location is loaded
 *
 * Loads @locations. Ignore non-existing locations
 *
 * Returns: (element-type Gedit.Document) (transfer container): the locations that were loaded
 */
GSList *
gedit_commands_load_locations (GeditWindow         *window,
			       const GSList        *locations,
			       const GeditEncoding *encoding,
			       gint                 line_pos,
			       gint                 column_pos)
{
	g_return_val_if_fail (GEDIT_IS_WINDOW (window), 0);
	g_return_val_if_fail ((locations != NULL) && (locations->data != NULL), 0);

	gedit_debug (DEBUG_COMMANDS);

	return load_file_list (window, locations, encoding, line_pos, column_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.
 */
GSList *
_gedit_cmd_load_files_from_prompt (GeditWindow         *window,
				   GSList              *files,
				   const GeditEncoding *encoding,
				   gint                 line_pos,
				   gint                 column_pos)
{
	gedit_debug (DEBUG_COMMANDS);

	return load_file_list (window, files, encoding, line_pos, column_pos, TRUE);
}

static void
open_dialog_destroyed (GeditWindow            *window,
		       GeditFileChooserDialog *dialog)
{
	gedit_debug (DEBUG_COMMANDS);

	g_object_set_data (G_OBJECT (window),
			   GEDIT_OPEN_DIALOG_KEY,
			   NULL);
}

static void
open_dialog_response_cb (GeditFileChooserDialog *dialog,
                         gint                    response_id,
                         GeditWindow            *window)
{
	GSList *files;
	const GeditEncoding *encoding;
	GSList *loaded;

	gedit_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 = gedit_file_chooser_dialog_get_encoding (dialog);

	gtk_widget_destroy (GTK_WIDGET (dialog));

	/* Remember the folder we navigated to */
	 _gedit_window_set_default_location (window, files->data);

	loaded = gedit_commands_load_locations (window,
	                                        files,
	                                        encoding,
	                                        0,
	                                        0);

	g_slist_free (loaded);
	g_slist_free_full (files, g_object_unref);
}

void
_gedit_cmd_file_open (GSimpleAction *action,
                      GVariant      *parameter,
                      gpointer       user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);
	GtkWidget *open_dialog;
	gpointer data;
	GeditDocument *doc;
	GFile *default_path = NULL;

	gedit_debug (DEBUG_COMMANDS);

	data = g_object_get_data (G_OBJECT (window), GEDIT_OPEN_DIALOG_KEY);

	if (data != NULL)
	{
		g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (data));

		gtk_window_present (GTK_WINDOW (data));

		return;
	}

	/* Translators: "Open" is the title of the file chooser window */
	open_dialog = gedit_file_chooser_dialog_new (_("Open"),
						     GTK_WINDOW (window),
						     GTK_FILE_CHOOSER_ACTION_OPEN,
						     NULL,
						     _("_Cancel"), GTK_RESPONSE_CANCEL,
						     _("_Open"), GTK_RESPONSE_OK,
						     NULL);

	g_object_set_data (G_OBJECT (window),
			   GEDIT_OPEN_DIALOG_KEY,
			   open_dialog);

	g_object_weak_ref (G_OBJECT (open_dialog),
			   (GWeakNotify) open_dialog_destroyed,
			   window);

	/* Set the curret folder uri */
	doc = gedit_window_get_active_document (window);
	if (doc != NULL)
	{
		GFile *file;

		file = gedit_document_get_location (doc);

		if (file != NULL)
		{
			default_path = g_file_get_parent (file);
			g_object_unref (file);
		}
	}

	if (default_path == NULL)
		default_path = _gedit_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);
}

void
_gedit_cmd_file_reopen_closed_tab (GSimpleAction *action,
                                   GVariant      *parameter,
                                   gpointer      user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);
	GFile *file;

	file = _gedit_window_pop_last_closed_doc (window);
	if (file != NULL)
	{
		gedit_commands_load_location (window, file, NULL, 0, 0);
	}
}

/* File saving */

static gboolean
is_read_only (GFile *location)
{
	gboolean ret = TRUE; /* default to read only */
	GFileInfo *info;

	gedit_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;

	gedit_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 = gedit_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_buttons (GTK_DIALOG (dialog),
			        _("_Cancel"), GTK_RESPONSE_CANCEL,
				_("_Replace"), GTK_RESPONSE_YES,
				NULL);

	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 gboolean
change_compression (GtkWindow *parent,
                    GFile     *file,
                    gboolean   compressed)
{
	GtkWidget *dialog;
	gint ret;
	gchar *parse_name;
	gchar *name_for_display;
	const gchar *primary_message;
	const gchar *secondary_message;
	const gchar *button_label;

	gedit_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 = gedit_utils_str_middle_truncate (parse_name, 50);
	g_free (parse_name);

	if (compressed)
	{
		primary_message = _("Save the file using compression?");
		secondary_message = _("The file \"%s\" was previously saved as plain "
		                      "text and will now be saved using compression.");
		button_label = _("_Save Using Compression");
	}
	else
	{
		primary_message = _("Save the file as plain text?");
		secondary_message = _("The file \"%s\" was previously saved "
		                      "using compression and will now be saved as plain text.");
		button_label = _("_Save As Plain Text");
	}

	dialog = gtk_message_dialog_new (parent,
					 GTK_DIALOG_DESTROY_WITH_PARENT,
					 GTK_MESSAGE_QUESTION,
					 GTK_BUTTONS_NONE,
					 "%s",
					 primary_message);

	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
						  secondary_message,
						  name_for_display);

	g_free (name_for_display);

	gtk_dialog_add_buttons (GTK_DIALOG (dialog),
			        _("_Cancel"), GTK_RESPONSE_CANCEL,
				button_label, GTK_RESPONSE_YES,
				NULL);

	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 GeditDocumentCompressionType
get_compression_type_from_file (GFile *file)
{
	gchar *name;
	gchar *content_type;
	GeditDocumentCompressionType type;

	name = g_file_get_basename (file);
	content_type = g_content_type_guess (name, NULL, 0, NULL);

	type = gedit_utils_get_compression_type_from_content_type (content_type);

	g_free (name);
	g_free (content_type);

	return type;
}

static void
save_dialog_response_cb (GeditFileChooserDialog *dialog,
                         gint                    response_id,
                         GeditWindow            *window)
{
	GeditTab *tab;
	gpointer data;
	GSList *tabs_to_save_as;

	gedit_debug (DEBUG_COMMANDS);

	tab = GEDIT_TAB (g_object_get_data (G_OBJECT (dialog),
					    GEDIT_TAB_TO_SAVE_AS));

	if (response_id != GTK_RESPONSE_OK)
	{
		gtk_widget_destroy (GTK_WIDGET (dialog));

		goto save_next_tab;
	}

	if (tab != NULL)
	{
		GFile *file;
		GeditDocument *doc;
		gchar *parse_name;
		GeditDocumentNewlineType newline_type;
		GeditDocumentCompressionType compression_type;
		GeditDocumentCompressionType current_compression_type;
		const GeditEncoding *encoding;

		doc = gedit_tab_get_document (tab);

		file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
		g_return_if_fail (file != NULL);

		compression_type = get_compression_type_from_file (file);
		current_compression_type = gedit_document_get_compression_type (doc);

		if ((compression_type == GEDIT_DOCUMENT_COMPRESSION_TYPE_NONE) !=
		    (current_compression_type == GEDIT_DOCUMENT_COMPRESSION_TYPE_NONE))
		{
			if (!change_compression (GTK_WINDOW (dialog),
			                         file,
			                         compression_type != GEDIT_DOCUMENT_COMPRESSION_TYPE_NONE))
			{
				gtk_widget_destroy (GTK_WIDGET (dialog));
				g_object_unref (file);

				goto save_next_tab;
			}
		}

		encoding = gedit_file_chooser_dialog_get_encoding (dialog);
		newline_type = gedit_file_chooser_dialog_get_newline_type (dialog);

		gtk_widget_destroy (GTK_WIDGET (dialog));

		doc = gedit_tab_get_document (tab);
		g_return_if_fail (GEDIT_IS_DOCUMENT (doc));

		parse_name = g_file_get_parse_name (file);

		gedit_statusbar_flash_message (GEDIT_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... */
		 _gedit_window_set_default_location (window, file);

		_gedit_tab_save_as (tab, file, encoding, newline_type, compression_type);

		g_object_unref (file);
	}

save_next_tab:

	data = g_object_get_data (G_OBJECT (window),
				  GEDIT_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 == GEDIT_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),
			   GEDIT_LIST_OF_TABS_TO_SAVE_AS,
			   tabs_to_save_as);

	if (tabs_to_save_as != NULL)
	{
		tab = GEDIT_TAB (tabs_to_save_as->data);

		if (GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (tab),
							    GEDIT_IS_CLOSING_TAB)) == TRUE)
		{
			g_object_set_data (G_OBJECT (tab),
					   GEDIT_IS_CLOSING_TAB,
					   NULL);

			/* Trace tab state changes */
			g_signal_connect (tab,
					  "notify::state",
					  G_CALLBACK (tab_state_changed_while_saving),
					  window);
		}

		gedit_window_set_active_tab (window, tab);
		_gedit_cmd_file_save_as_tab (tab, window);
	}
}

static GtkFileChooserConfirmation
confirm_overwrite_callback (GtkFileChooser *dialog,
			    gpointer        data)
{
	gchar *uri;
	GFile *file;
	GtkFileChooserConfirmation res;

	gedit_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;
}

void
_gedit_cmd_file_save_as_tab (GeditTab    *tab,
                             GeditWindow *window)
{
	GtkWidget *save_dialog;
	GtkWindowGroup *wg;
	GeditDocument *doc;
	GFile *file;
	const GeditEncoding *encoding;
	GeditDocumentNewlineType newline_type;

	g_return_if_fail (GEDIT_IS_TAB (tab));
	g_return_if_fail (GEDIT_IS_WINDOW (window));

	gedit_debug (DEBUG_COMMANDS);

	save_dialog = gedit_file_chooser_dialog_new (_("Save As"),
						     GTK_WINDOW (window),
						     GTK_FILE_CHOOSER_ACTION_SAVE,
						     NULL,
						     _("_Cancel"), GTK_RESPONSE_CANCEL,
						     _("_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 = gedit_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 = gedit_tab_get_document (tab);
	file = gedit_document_get_location (doc);

	if (file != NULL)
	{
		gtk_file_chooser_set_file (GTK_FILE_CHOOSER (save_dialog),
					   file,
					   NULL);

		g_object_unref (file);
	}
	else
	{
		GFile *default_path;
		gchar *docname;

		default_path = _gedit_window_get_default_location (window);
		docname = gedit_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 = gedit_document_get_encoding (doc);
	g_return_if_fail (encoding != NULL);

	newline_type = gedit_document_get_newline_type (doc);

	gedit_file_chooser_dialog_set_encoding (GEDIT_FILE_CHOOSER_DIALOG (save_dialog),
						encoding);

	gedit_file_chooser_dialog_set_newline_type (GEDIT_FILE_CHOOSER_DIALOG (save_dialog),
	                                            newline_type);

	g_object_set_data (G_OBJECT (save_dialog),
			   GEDIT_TAB_TO_SAVE_AS,
			   tab);

	g_signal_connect (save_dialog,
			  "response",
			  G_CALLBACK (save_dialog_response_cb),
			  window);

	gtk_widget_show (save_dialog);
}

void
_gedit_cmd_file_save_tab (GeditTab    *tab,
                          GeditWindow *window)
{
	GeditDocument *doc;
	gchar *uri_for_display;

	gedit_debug (DEBUG_COMMANDS);

	g_return_if_fail (GEDIT_IS_TAB (tab));
	g_return_if_fail (GEDIT_IS_WINDOW (window));

	doc = gedit_tab_get_document (tab);
	g_return_if_fail (GEDIT_IS_DOCUMENT (doc));

	if (gedit_document_is_untitled (doc) ||
	    gedit_document_get_readonly (doc))
	{
		gedit_debug_message (DEBUG_COMMANDS, "Untitled or Readonly");

		_gedit_cmd_file_save_as_tab (tab, window);

		return;
	}

	uri_for_display = gedit_document_get_uri_for_display (doc);
	gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar),
				        window->priv->generic_message_cid,
				       _("Saving file '%s'\342\200\246"),
				       uri_for_display);

	g_free (uri_for_display);

	_gedit_tab_save (tab);
}

void
_gedit_cmd_file_save (GSimpleAction *action,
                      GVariant      *parameter,
                      gpointer       user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);
	GeditTab *tab;

	gedit_debug (DEBUG_COMMANDS);

	tab = gedit_window_get_active_tab (window);
	if (tab == NULL)
		return;

	_gedit_cmd_file_save_tab (tab, window);
}

void
_gedit_cmd_file_save_as (GSimpleAction *action,
                         GVariant      *parameter,
                         gpointer       user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);
	GeditTab *tab;

	gedit_debug (DEBUG_COMMANDS);

	tab = gedit_window_get_active_tab (window);
	if (tab == NULL)
		return;

	_gedit_cmd_file_save_as_tab (tab, window);
}

/*
 * The docs in the list must belong to the same GeditWindow.
 */
void
_gedit_cmd_file_save_documents_list (GeditWindow *window,
				     GList       *docs)
{
	GList *l;
	GSList *tabs_to_save_as = NULL;

	gedit_debug (DEBUG_COMMANDS);

	g_return_if_fail (!(gedit_window_get_state (window) &
			    GEDIT_WINDOW_STATE_PRINTING));

	l = docs;
	while (l != NULL)
	{
		GeditDocument *doc;
		GeditTab *t;
		GeditTabState state;

		g_return_if_fail (GEDIT_IS_DOCUMENT (l->data));

		doc = GEDIT_DOCUMENT (l->data);
		t = gedit_tab_get_from_document (doc);
		state = gedit_tab_get_state (t);

		g_return_if_fail (state != GEDIT_TAB_STATE_PRINTING);
		g_return_if_fail (state != GEDIT_TAB_STATE_PRINT_PREVIEWING);
		g_return_if_fail (state != GEDIT_TAB_STATE_CLOSING);

		if ((state == GEDIT_TAB_STATE_NORMAL) ||
		    (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) ||
		    (state == GEDIT_TAB_STATE_GENERIC_NOT_EDITABLE))
		{
			if (_gedit_document_needs_saving (doc))
			{
				/* FIXME: manage the case of local readonly files owned by the
				   user is running gedit - Paolo (Dec. 8, 2005) */
				if (gedit_document_is_untitled (doc) ||
				    gedit_document_get_readonly (doc))
				{
					tabs_to_save_as = g_slist_prepend (tabs_to_save_as,
									   t);
				}
				else
				{
					_gedit_cmd_file_save_tab (t, window);
				}
			}
		}
		else
		{
			/* If the state is:
			   - GEDIT_TAB_STATE_LOADING: we do not save since we are sure the file is unmodified
			   - GEDIT_TAB_STATE_REVERTING: we do not save since the user wants
			     to return back to the version of the file she previously saved
			   - GEDIT_TAB_STATE_SAVING: well, we are already saving (no need to save again)
			   - GEDIT_TAB_STATE_PRINTING, GEDIT_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.
			   - GEDIT_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)
			   - GEDIT_TAB_STATE_LOADING_ERROR: there is nothing to save
			   - GEDIT_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
			   - GEDIT_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
			   - GEDIT_TAB_STATE_CLOSING: this state is invalid in this case
			*/

			gchar *uri_for_display;

			uri_for_display = gedit_document_get_uri_for_display (doc);
			gedit_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)
	{
		GeditTab *tab;

		tabs_to_save_as = g_slist_reverse (tabs_to_save_as );

		g_return_if_fail (g_object_get_data (G_OBJECT (window),
						     GEDIT_LIST_OF_TABS_TO_SAVE_AS) == NULL);

		g_object_set_data (G_OBJECT (window),
				   GEDIT_LIST_OF_TABS_TO_SAVE_AS,
				   tabs_to_save_as);

		tab = GEDIT_TAB (tabs_to_save_as->data);

		gedit_window_set_active_tab (window, tab);
		_gedit_cmd_file_save_as_tab (tab, window);
	}
}

void
gedit_commands_save_all_documents (GeditWindow *window)
{
	GList *docs;

	g_return_if_fail (GEDIT_IS_WINDOW (window));

	gedit_debug (DEBUG_COMMANDS);

	docs = gedit_window_get_documents (window);

	_gedit_cmd_file_save_documents_list (window, docs);

	g_list_free (docs);
}

void
_gedit_cmd_file_save_all (GSimpleAction *action,
                          GVariant      *parameter,
                          gpointer       user_data)
{
	gedit_commands_save_all_documents (GEDIT_WINDOW (user_data));
}

void
gedit_commands_save_document (GeditWindow   *window,
                              GeditDocument *document)
{
	GeditTab *tab;

	g_return_if_fail (GEDIT_IS_WINDOW (window));
	g_return_if_fail (GEDIT_IS_DOCUMENT (document));

	gedit_debug (DEBUG_COMMANDS);

	tab = gedit_tab_get_from_document (document);
	_gedit_cmd_file_save_tab (tab, window);
}

/* File revert */
static void
do_revert (GeditWindow *window,
	   GeditTab    *tab)
{
	GeditDocument *doc;
	gchar *docname;

	gedit_debug (DEBUG_COMMANDS);

	doc = gedit_tab_get_document (tab);
	docname = gedit_document_get_short_name_for_display (doc);

	gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar),
				        window->priv->generic_message_cid,
				       _("Reverting the document '%s'\342\200\246"),
				       docname);

	g_free (docname);

	_gedit_tab_revert (tab);
}

static void
revert_dialog_response_cb (GtkDialog   *dialog,
			   gint         response_id,
			   GeditWindow *window)
{
	GeditTab *tab;

	gedit_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 = gedit_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 (GeditWindow   *window,
	       GeditDocument *doc)
{
	GtkWidget *dialog;
	gchar *docname;
	gchar *primary_msg;
	gchar *secondary_msg;
	glong seconds;

	gedit_debug (DEBUG_COMMANDS);

	docname = gedit_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, _gedit_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_buttons (GTK_DIALOG (dialog),
			        _("_Cancel"), GTK_RESPONSE_CANCEL,
				_("_Revert"), GTK_RESPONSE_OK,
				NULL);

	gtk_dialog_set_default_response	(GTK_DIALOG (dialog),
					 GTK_RESPONSE_CANCEL);

	return dialog;
}

void
_gedit_cmd_file_revert (GSimpleAction *action,
                        GVariant      *parameter,
                        gpointer       user_data)
{
	GeditWindow    *window = GEDIT_WINDOW (user_data);
	GeditTab       *tab;
	GeditDocument  *doc;
	GtkWidget      *dialog;
	GtkWindowGroup *wg;

	gedit_debug (DEBUG_COMMANDS);

	tab = gedit_window_get_active_tab (window);
	g_return_if_fail (tab != NULL);

	/* If we are already displaying a notification
	 * reverting will drop local modifications or
	 * if the document has not been modified, do
	 * not bug the user further */
	if (gedit_tab_get_state (tab) == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION ||
	    _gedit_tab_get_can_close (tab))
	{
		do_revert (window, tab);
		return;
	}

	doc = gedit_tab_get_document (tab);
	g_return_if_fail (doc != NULL);
	g_return_if_fail (!gedit_document_is_untitled (doc));

	dialog = revert_dialog (window, doc);

	wg = gedit_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 (GeditTab *tab)
{
	GtkWidget *toplevel;
	GeditWindow *window;

	gedit_debug (DEBUG_COMMANDS);

	g_return_val_if_fail (gedit_tab_get_state (tab) == GEDIT_TAB_STATE_CLOSING,
			      FALSE);

	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tab));
	g_return_val_if_fail (GEDIT_IS_WINDOW (toplevel), FALSE);

	window = GEDIT_WINDOW (toplevel);

	gedit_window_close_tab (window, tab);

	if (gedit_window_get_active_tab (window) == NULL)
	{
		gboolean is_quitting;

		is_quitting = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window),
								      GEDIT_IS_QUITTING));

		if (is_quitting)
			gtk_widget_destroy (GTK_WIDGET (window));
	}

	return FALSE;
}

static void
tab_state_changed_while_saving (GeditTab    *tab,
				GParamSpec  *pspec,
				GeditWindow *window)
{
	GeditTabState ts;

	ts = gedit_tab_get_state (tab);

	gedit_debug_message (DEBUG_COMMANDS, "State while saving: %d\n", ts);

	/* When the state become NORMAL, it means the saving operation is
	   finished */
	if (ts == GEDIT_TAB_STATE_NORMAL)
	{
		GeditDocument *doc;

		g_signal_handlers_disconnect_by_func (tab,
						      G_CALLBACK (tab_state_changed_while_saving),
					      	      window);

		doc = gedit_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 (_gedit_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 */
		_gedit_tab_mark_for_closing (tab);

		g_idle_add_full (G_PRIORITY_HIGH_IDLE,
				 (GSourceFunc)really_close_tab,
				 tab,
				 NULL);
	}
}

static void
save_and_close (GeditTab    *tab,
		GeditWindow *window)
{
	gedit_debug (DEBUG_COMMANDS);

	/* Trace tab state changes */
	g_signal_connect (tab,
			  "notify::state",
			  G_CALLBACK (tab_state_changed_while_saving),
			  window);

	_gedit_cmd_file_save_tab (tab, window);
}

static void
save_as_and_close (GeditTab    *tab,
		   GeditWindow *window)
{
	gedit_debug (DEBUG_COMMANDS);

	g_object_set_data (G_OBJECT (tab),
			   GEDIT_IS_CLOSING_TAB,
			   NULL);

	/* Trace tab state changes */
	g_signal_connect (tab,
			  "notify::state",
			  G_CALLBACK (tab_state_changed_while_saving),
			  window);

	gedit_window_set_active_tab (window, tab);
	_gedit_cmd_file_save_as_tab (tab, window);
}

static void
save_and_close_documents (GList         *docs,
                          GeditWindow   *window,
                          GeditNotebook *notebook)
{
	GList *tabs;
	GList *l;
	GSList *sl;
	GSList *tabs_to_save_as;
	GSList *tabs_to_save_and_close;
	GList *tabs_to_close;

	gedit_debug (DEBUG_COMMANDS);

	g_return_if_fail (!(gedit_window_get_state (window) & GEDIT_WINDOW_STATE_PRINTING));

	if (notebook != NULL)
	{
		tabs = gtk_container_get_children (GTK_CONTAINER (notebook));
	}
	else
	{
		tabs = _gedit_window_get_all_tabs (window);
	}

	tabs_to_save_as = NULL;
	tabs_to_save_and_close = NULL;
	tabs_to_close = NULL;

	l = tabs;
	while (l != NULL)
	{
		GeditTab *t;
		GeditTabState state;
		GeditDocument *doc;

		t = GEDIT_TAB (l->data);

		state = gedit_tab_get_state (t);
		doc = gedit_tab_get_document (t);

		/* If the state is: ([*] invalid states)
		   - GEDIT_TAB_STATE_NORMAL: close (and if needed save)
		   - GEDIT_TAB_STATE_LOADING: close, we are sure the file is unmodified
		   - GEDIT_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)
		   - [*] GEDIT_TAB_STATE_SAVING: invalid, ClosAll
		     and Quit are unsensitive if the window state is SAVING.
		   - [*] GEDIT_TAB_STATE_PRINTING, GEDIT_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.
		   - GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW: close (and if needed save)
		   - GEDIT_TAB_STATE_LOADING_ERROR: close without saving (if the state is LOADING_ERROR then the
		     document is not modified)
		   - GEDIT_TAB_STATE_REVERTING_ERROR: we do not close since the document contains errors
		   - GEDIT_TAB_STATE_SAVING_ERROR: we do not close since the document contains errors
		   - GEDIT_TAB_STATE_GENERIC_ERROR: we do not close since the document contains
		     errors (CHECK: we should problably remove this state)
		   - [*] GEDIT_TAB_STATE_CLOSING: this state is invalid in this case
		*/

		g_return_if_fail (state != GEDIT_TAB_STATE_PRINTING);
		g_return_if_fail (state != GEDIT_TAB_STATE_PRINT_PREVIEWING);
		g_return_if_fail (state != GEDIT_TAB_STATE_CLOSING);
		g_return_if_fail (state != GEDIT_TAB_STATE_SAVING);

		if ((state != GEDIT_TAB_STATE_SAVING_ERROR) &&
		    (state != GEDIT_TAB_STATE_GENERIC_ERROR) &&
		    (state != GEDIT_TAB_STATE_REVERTING_ERROR))
		{
			if ((g_list_index ((GList *)docs, doc) >= 0) &&
			    (state != GEDIT_TAB_STATE_LOADING) &&
			    (state != GEDIT_TAB_STATE_LOADING_ERROR) &&
			    (state != GEDIT_TAB_STATE_REVERTING)) /* CHECK: is this the right behavior with REVERTING ?*/
			{
				/* The document must be saved before closing */
				g_return_if_fail (_gedit_document_needs_saving (doc));

				/* FIXME: manage the case of local readonly files owned by the
				   user is running gedit - Paolo (Dec. 8, 2005) */
				if (gedit_document_is_untitled (doc) ||
				    gedit_document_get_readonly (doc))
				{
					g_object_set_data (G_OBJECT (t),
							   GEDIT_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) */
	gedit_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 (GEDIT_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)
	{
		GeditTab *tab;

		tabs_to_save_as = g_slist_reverse (tabs_to_save_as );

		g_return_if_fail (g_object_get_data (G_OBJECT (window),
						     GEDIT_LIST_OF_TABS_TO_SAVE_AS) == NULL);

		g_object_set_data (G_OBJECT (window),
				   GEDIT_LIST_OF_TABS_TO_SAVE_AS,
				   tabs_to_save_as);

		tab = GEDIT_TAB (tabs_to_save_as->data);

		save_as_and_close (tab, window);
	}
}

static void
save_and_close_document (const GList  *docs,
			 GeditWindow  *window)
{
	GeditTab *tab;

	gedit_debug (DEBUG_COMMANDS);

	g_return_if_fail (docs->next == NULL);

	tab = gedit_tab_get_from_document (GEDIT_DOCUMENT (docs->data));
	g_return_if_fail (tab != NULL);

	save_and_close (tab, window);
}

static void
close_all_tabs (GeditWindow *window)
{
	gboolean is_quitting;

	gedit_debug (DEBUG_COMMANDS);

	/* There is no document to save -> close all tabs */
	gedit_window_close_all_tabs (window);

	is_quitting = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window),
							      GEDIT_IS_QUITTING));

	if (is_quitting)
		gtk_widget_destroy (GTK_WIDGET (window));

	return;
}

static void
close_document (GeditWindow   *window,
		GeditDocument *doc)
{
	GeditTab *tab;

	gedit_debug (DEBUG_COMMANDS);

	tab = gedit_tab_get_from_document (doc);
	g_return_if_fail (tab != NULL);

	gedit_window_close_tab (window, tab);
}

static void
close_confirmation_dialog_response_handler (GeditCloseConfirmationDialog *dlg,
                                            gint                          response_id,
                                            GeditWindow                  *window)
{
	GList *selected_documents;
	gboolean is_closing_all;
	GeditNotebook *notebook_to_close;

	gedit_debug (DEBUG_COMMANDS);

	is_closing_all = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window),
	                                                         GEDIT_IS_CLOSING_ALL));

	notebook_to_close = g_object_get_data (G_OBJECT (window), GEDIT_NOTEBOOK_TO_CLOSE);

	gtk_widget_hide (GTK_WIDGET (dlg));

	switch (response_id)
	{
		case GTK_RESPONSE_YES: /* Save and Close */
			selected_documents = gedit_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 gedit window */
					gtk_widget_destroy (GTK_WIDGET (dlg));

					close_all_tabs (window);

					return;
				}
				else if (notebook_to_close)
				{
					gedit_notebook_remove_all_tabs (notebook_to_close);
				}
				else
				{
					g_return_if_reached ();
				}
			}
			else
			{
				if (is_closing_all || notebook_to_close)
				{
					GeditNotebook *nb = is_closing_all ? NULL : notebook_to_close;

					save_and_close_documents (selected_documents, window, nb);
				}
				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 gedit window */
				gtk_widget_destroy (GTK_WIDGET (dlg));

				close_all_tabs (window);

				return;
			}
			else if (notebook_to_close)
			{
				gedit_notebook_remove_all_tabs (notebook_to_close);
			}
			else
			{
				const GList *unsaved_documents;

				unsaved_documents = gedit_close_confirmation_dialog_get_unsaved_documents (dlg);
				g_return_if_fail (unsaved_documents->next == NULL);

				close_document (window, GEDIT_DOCUMENT (unsaved_documents->data));
			}

			break;

		default: /* Do not close */

			/* Reset is_quitting flag */
			g_object_set_data (G_OBJECT (window),
			                   GEDIT_IS_QUITTING,
			                   GBOOLEAN_TO_POINTER (FALSE));

#ifdef OS_OSX
			g_object_set_data (G_OBJECT (window),
			                   GEDIT_IS_QUITTING_ALL,
			                   GINT_TO_POINTER (FALSE));
#endif
			break;
	}

	g_object_set_data (G_OBJECT (window), GEDIT_NOTEBOOK_TO_CLOSE, NULL);

	gtk_widget_destroy (GTK_WIDGET (dlg));
}

/* Returns TRUE if the tab can be immediately closed */
static gboolean
tab_can_close (GeditTab  *tab,
	       GtkWindow *window)
{
	GeditDocument *doc;

	gedit_debug (DEBUG_COMMANDS);

	doc = gedit_tab_get_document (tab);

	if (!_gedit_tab_get_can_close (tab))
	{
		GtkWidget *dlg;

		dlg = gedit_close_confirmation_dialog_new_single (window, doc);
		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
 * gedit_window_close_tab always run the confirm dialog?
 * we should not allow closing a tab without resetting the
 * GEDIT_IS_CLOSING_ALL flag!
 */
void
_gedit_cmd_file_close_tab (GeditTab    *tab,
			   GeditWindow *window)
{
	gedit_debug (DEBUG_COMMANDS);

	g_return_if_fail (GTK_WIDGET (window) == gtk_widget_get_toplevel (GTK_WIDGET (tab)));

	g_object_set_data (G_OBJECT (window),
			   GEDIT_IS_CLOSING_ALL,
			   GBOOLEAN_TO_POINTER (FALSE));

	g_object_set_data (G_OBJECT (window),
			   GEDIT_IS_QUITTING,
			   GBOOLEAN_TO_POINTER (FALSE));

	g_object_set_data (G_OBJECT (window),
	                   GEDIT_IS_QUITTING_ALL,
	                   GINT_TO_POINTER (FALSE));

	if (tab_can_close (tab, GTK_WINDOW (window)))
	{
		gedit_window_close_tab (window, tab);
	}
}

void
_gedit_cmd_file_close (GSimpleAction *action,
                       GVariant      *parameter,
                       gpointer       user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);
	GeditTab *active_tab;

	gedit_debug (DEBUG_COMMANDS);

	active_tab = gedit_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;
	}

	_gedit_cmd_file_close_tab (active_tab, window);
}

static void
file_close_dialog (GeditWindow *window,
                   GList       *unsaved_docs)
{
	GtkWidget *dlg;

	if (unsaved_docs->next == NULL)
	{
		/* There is only one unsaved document */
		GeditTab      *tab;
		GeditDocument *doc;

		doc = GEDIT_DOCUMENT (unsaved_docs->data);

		tab = gedit_tab_get_from_document (doc);
		g_return_if_fail (tab != NULL);

		gedit_window_set_active_tab (window, tab);

		dlg = gedit_close_confirmation_dialog_new_single (GTK_WINDOW (window), doc);
	}
	else
	{
		dlg = gedit_close_confirmation_dialog_new (GTK_WINDOW (window), unsaved_docs);
	}

	g_signal_connect (dlg,
	                  "response",
	                  G_CALLBACK (close_confirmation_dialog_response_handler),
	                  window);

	gtk_widget_show (dlg);
}

static GList *
notebook_get_unsaved_documents (GeditNotebook *notebook)
{
	GList *children, *l;
	GList *unsaved_docs = NULL;

	children = gtk_container_get_children (GTK_CONTAINER (notebook));

	for (l = children; l != NULL; l = g_list_next (l))
	{
		if (!_gedit_tab_get_can_close (l->data))
		{
			GeditDocument *doc;

			doc = gedit_tab_get_document (l->data);
			unsaved_docs = g_list_prepend (unsaved_docs, doc);
		}
	}

	g_list_free (children);

	return g_list_reverse (unsaved_docs);
}

/* Close a notebook */
void
_gedit_cmd_file_close_notebook (GeditWindow   *window,
                                GeditNotebook *notebook)
{
	GList *unsaved_docs;

	g_object_set_data (G_OBJECT (window), GEDIT_IS_CLOSING_ALL, GBOOLEAN_TO_POINTER (FALSE));
	g_object_set_data (G_OBJECT (window), GEDIT_IS_QUITTING, GBOOLEAN_TO_POINTER (FALSE));
	g_object_set_data (G_OBJECT (window), GEDIT_IS_QUITTING_ALL, GINT_TO_POINTER (FALSE));

	g_object_set_data (G_OBJECT (window), GEDIT_NOTEBOOK_TO_CLOSE, notebook);

	unsaved_docs = notebook_get_unsaved_documents (notebook);

	if (unsaved_docs == NULL)
	{
		/* There is no document to save -> close the notebook */
		gedit_notebook_remove_all_tabs (GEDIT_NOTEBOOK (notebook));
	}
	else
	{
		file_close_dialog (window, unsaved_docs);

		g_list_free (unsaved_docs);
	}
}

/* Close all tabs */
static void
file_close_all (GeditWindow *window,
                gboolean     is_quitting)
{
	GList     *unsaved_docs;

	gedit_debug (DEBUG_COMMANDS);

	g_return_if_fail (!(gedit_window_get_state (window) &
	                    (GEDIT_WINDOW_STATE_SAVING |
	                     GEDIT_WINDOW_STATE_PRINTING)));

	g_object_set_data (G_OBJECT (window),
			   GEDIT_IS_CLOSING_ALL,
			   GBOOLEAN_TO_POINTER (TRUE));

	g_object_set_data (G_OBJECT (window),
			   GEDIT_IS_QUITTING,
			   GBOOLEAN_TO_POINTER (is_quitting));

	unsaved_docs = gedit_window_get_unsaved_documents (window);

	if (unsaved_docs != NULL)
	{
		file_close_dialog (window, unsaved_docs);

		g_list_free (unsaved_docs);
	}
	else
	{
		/* There is no document to save -> close all tabs */
		gedit_window_close_all_tabs (window);

		if (is_quitting)
		{
			gtk_widget_destroy (GTK_WIDGET (window));
		}
	}
}

void
_gedit_cmd_file_close_all (GSimpleAction *action,
                           GVariant      *parameter,
                           gpointer       user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);

	gedit_debug (DEBUG_COMMANDS);

	g_return_if_fail (!(gedit_window_get_state (window) &
	                    (GEDIT_WINDOW_STATE_SAVING |
	                    GEDIT_WINDOW_STATE_PRINTING)));

	file_close_all (window, FALSE);
}

/* Quit */
static void
quit_all (void)
{
	GList *windows;
	GList *l;

	windows = gedit_app_get_main_windows (GEDIT_APP (g_application_get_default ()));

	for (l = windows; l != NULL; l = g_list_next (l))
	{
		GeditWindow *window = l->data;

		g_object_set_data (G_OBJECT (window),
		                   GEDIT_IS_QUITTING_ALL,
		                   GINT_TO_POINTER (TRUE));

		if (!(gedit_window_get_state (window) &
		      (GEDIT_WINDOW_STATE_SAVING | GEDIT_WINDOW_STATE_PRINTING)))
		{
			file_close_all (window, TRUE);
		}
	}

	g_list_free (windows);
}

void
_gedit_cmd_file_quit (GSimpleAction *action,
                      GVariant      *parameter,
                      gpointer       user_data)
{
	GeditWindow *window = GEDIT_WINDOW (user_data);

	gedit_debug (DEBUG_COMMANDS);

	if (window == NULL)
	{
		quit_all ();
		return;
	}

	g_return_if_fail (!(gedit_window_get_state (window) &
	                    (GEDIT_WINDOW_STATE_SAVING |
	                     GEDIT_WINDOW_STATE_PRINTING)));

	file_close_all (window, TRUE);
}

/* ex:set ts=8 noet: */
