/*
 * xedit-tab.c
 * This file is part of xedit
 *
 * Copyright (C) 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., 51 Franklin St, Fifth Floor, 
 * Boston, MA 02110-1301, USA.
 */
 
/*
 * Modified by the xedit Team, 2005. See the AUTHORS file for a 
 * list of people on the xedit Team.  
 * See the ChangeLog files for a list of changes. 
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib/gi18n.h>
#include <gio/gio.h>

#include "xedit-app.h"
#include "xedit-notebook.h"
#include "xedit-tab.h"
#include "xedit-utils.h"
#include "xedit-io-error-message-area.h"
#include "xedit-print-job.h"
#include "xedit-print-preview.h"
#include "xedit-progress-message-area.h"
#include "xedit-debug.h"
#include "xedit-prefs-manager-app.h"
#include "xedit-enum-types.h"

#define XEDIT_TAB_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), XEDIT_TYPE_TAB, XeditTabPrivate))

#define XEDIT_TAB_KEY "XEDIT_TAB_KEY"

#if GTK_CHECK_VERSION (3, 0, 0)
#define gdk_cursor_unref(cursor) g_object_unref (cursor)
#endif

struct _XeditTabPrivate
{
	XeditTabState	        state;
	
	GtkWidget	       *view;
	GtkWidget	       *view_scrolled_window;

	GtkWidget	       *message_area;
	GtkWidget	       *print_preview;

	XeditPrintJob          *print_job;

	/* tmp data for saving */
	gchar		       *tmp_save_uri;

	/* tmp data for loading */
	gint                    tmp_line_pos;
	const XeditEncoding    *tmp_encoding;
	
	GTimer 		       *timer;
	guint		        times_called;

	XeditDocumentSaveFlags	save_flags;

        gint                    auto_save_interval;
        guint                   auto_save_timeout;
        
	gint	                not_editable : 1;
	gint                    auto_save : 1;

	gint                    ask_if_externally_modified : 1;
};

#if GTK_CHECK_VERSION (3, 0, 0)
G_DEFINE_TYPE(XeditTab, xedit_tab, GTK_TYPE_BOX)
#else
G_DEFINE_TYPE(XeditTab, xedit_tab, GTK_TYPE_VBOX)
#endif

enum
{
	PROP_0,
	PROP_NAME,
	PROP_STATE,
	PROP_AUTO_SAVE,
	PROP_AUTO_SAVE_INTERVAL
};

static gboolean xedit_tab_auto_save (XeditTab *tab);

static void
install_auto_save_timeout (XeditTab *tab)
{
	gint timeout;

	xedit_debug (DEBUG_TAB);

	g_return_if_fail (tab->priv->auto_save_timeout <= 0);
	g_return_if_fail (tab->priv->auto_save);
	g_return_if_fail (tab->priv->auto_save_interval > 0);
	
	g_return_if_fail (tab->priv->state != XEDIT_TAB_STATE_LOADING);
	g_return_if_fail (tab->priv->state != XEDIT_TAB_STATE_SAVING);
	g_return_if_fail (tab->priv->state != XEDIT_TAB_STATE_REVERTING);
	g_return_if_fail (tab->priv->state != XEDIT_TAB_STATE_LOADING_ERROR);
	g_return_if_fail (tab->priv->state != XEDIT_TAB_STATE_SAVING_ERROR);
	g_return_if_fail (tab->priv->state != XEDIT_TAB_STATE_SAVING_ERROR);
	g_return_if_fail (tab->priv->state != XEDIT_TAB_STATE_REVERTING_ERROR);

	/* Add a new timeout */
	timeout = g_timeout_add_seconds (tab->priv->auto_save_interval * 60,
					 (GSourceFunc) xedit_tab_auto_save,
					 tab);

	tab->priv->auto_save_timeout = timeout;
}

static gboolean
install_auto_save_timeout_if_needed (XeditTab *tab)
{
	XeditDocument *doc;

	xedit_debug (DEBUG_TAB);
	
	g_return_val_if_fail (tab->priv->auto_save_timeout <= 0, FALSE);
	g_return_val_if_fail ((tab->priv->state == XEDIT_TAB_STATE_NORMAL) ||
			      (tab->priv->state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) ||
			      (tab->priv->state == XEDIT_TAB_STATE_CLOSING), FALSE);

	if (tab->priv->state == XEDIT_TAB_STATE_CLOSING)
		return FALSE;

	doc = xedit_tab_get_document (tab);

 	if (tab->priv->auto_save && 
 	    !xedit_document_is_untitled (doc) &&
 	    !xedit_document_get_readonly (doc))
 	{ 
 		install_auto_save_timeout (tab);
 		
 		return TRUE;
 	}
 	
 	return FALSE;
}

static void
remove_auto_save_timeout (XeditTab *tab)
{
	xedit_debug (DEBUG_TAB);

	/* FIXME: check sugli stati */
	
	g_return_if_fail (tab->priv->auto_save_timeout > 0);
	
	g_source_remove (tab->priv->auto_save_timeout);
	tab->priv->auto_save_timeout = 0;
}

static void
xedit_tab_get_property (GObject    *object,
		        guint       prop_id,
		        GValue     *value,
		        GParamSpec *pspec)
{
	XeditTab *tab = XEDIT_TAB (object);

	switch (prop_id)
	{
		case PROP_NAME:
			g_value_take_string (value,
					     _xedit_tab_get_name (tab));
			break;
		case PROP_STATE:
			g_value_set_enum (value,
					  xedit_tab_get_state (tab));
			break;			
		case PROP_AUTO_SAVE:
			g_value_set_boolean (value,
					     xedit_tab_get_auto_save_enabled (tab));
			break;
		case PROP_AUTO_SAVE_INTERVAL:
			g_value_set_int (value,
					 xedit_tab_get_auto_save_interval (tab));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;			
	}
}

static void
xedit_tab_set_property (GObject      *object,
		        guint         prop_id,
		        const GValue *value,
		        GParamSpec   *pspec)
{
	XeditTab *tab = XEDIT_TAB (object);

	switch (prop_id)
	{
		case PROP_AUTO_SAVE:
			xedit_tab_set_auto_save_enabled (tab,
							 g_value_get_boolean (value));
			break;
		case PROP_AUTO_SAVE_INTERVAL:
			xedit_tab_set_auto_save_interval (tab,
							  g_value_get_int (value));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;			
	}
}

static void
xedit_tab_finalize (GObject *object)
{
	XeditTab *tab = XEDIT_TAB (object);

	if (tab->priv->timer != NULL)
		g_timer_destroy (tab->priv->timer);

	g_free (tab->priv->tmp_save_uri);

	if (tab->priv->auto_save_timeout > 0)
		remove_auto_save_timeout (tab);

	G_OBJECT_CLASS (xedit_tab_parent_class)->finalize (object);
}

static void 
xedit_tab_class_init (XeditTabClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->finalize = xedit_tab_finalize;
	object_class->get_property = xedit_tab_get_property;
	object_class->set_property = xedit_tab_set_property;
	
	g_object_class_install_property (object_class,
					 PROP_NAME,
					 g_param_spec_string ("name",
							      "Name",
							      "The tab's name",
							      NULL,
							      G_PARAM_READABLE |
							      G_PARAM_STATIC_STRINGS));

	g_object_class_install_property (object_class,
					 PROP_STATE,
					 g_param_spec_enum ("state",
							    "State",
							    "The tab's state",
							    XEDIT_TYPE_TAB_STATE,
							    XEDIT_TAB_STATE_NORMAL,
							    G_PARAM_READABLE |
							    G_PARAM_STATIC_STRINGS));

	g_object_class_install_property (object_class,
					 PROP_AUTO_SAVE,
					 g_param_spec_boolean ("autosave",
							       "Autosave",
							       "Autosave feature",
							       TRUE,
							       G_PARAM_READWRITE |
							       G_PARAM_STATIC_STRINGS));

	g_object_class_install_property (object_class,
					 PROP_AUTO_SAVE_INTERVAL,
					 g_param_spec_int ("autosave-interval",
							   "AutosaveInterval",
							   "Time between two autosaves",
							   0,
							   G_MAXINT,
							   0,
							   G_PARAM_READWRITE |
							   G_PARAM_STATIC_STRINGS));

	g_type_class_add_private (object_class, sizeof (XeditTabPrivate));
}

/**
 * xedit_tab_get_state:
 * @tab: a #XeditTab
 *
 * Gets the #XeditTabState of @tab.
 *
 * Returns: the #XeditTabState of @tab
 */
XeditTabState
xedit_tab_get_state (XeditTab *tab)
{
	g_return_val_if_fail (XEDIT_IS_TAB (tab), XEDIT_TAB_STATE_NORMAL);
	
	return tab->priv->state;
}

static void
set_cursor_according_to_state (GtkTextView   *view,
			       XeditTabState  state)
{
	GdkCursor *cursor;
	GdkWindow *text_window;
	GdkWindow *left_window;

	text_window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_TEXT);
	left_window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_LEFT);

	if ((state == XEDIT_TAB_STATE_LOADING)          ||
	    (state == XEDIT_TAB_STATE_REVERTING)        ||
	    (state == XEDIT_TAB_STATE_SAVING)           ||
	    (state == XEDIT_TAB_STATE_PRINTING)         ||
	    (state == XEDIT_TAB_STATE_PRINT_PREVIEWING) ||
	    (state == XEDIT_TAB_STATE_CLOSING))
	{
		cursor = gdk_cursor_new_for_display (
				gtk_widget_get_display (GTK_WIDGET (view)),
				GDK_WATCH);

		if (text_window != NULL)
			gdk_window_set_cursor (text_window, cursor);
		if (left_window != NULL)
			gdk_window_set_cursor (left_window, cursor);

		gdk_cursor_unref (cursor);
	}
	else
	{
		cursor = gdk_cursor_new_for_display (
				gtk_widget_get_display (GTK_WIDGET (view)),
				GDK_XTERM);

		if (text_window != NULL)
			gdk_window_set_cursor (text_window, cursor);
		if (left_window != NULL)
			gdk_window_set_cursor (left_window, NULL);

		gdk_cursor_unref (cursor);
	}
}

static void
view_realized (GtkTextView *view,
	       XeditTab    *tab)
{
	set_cursor_according_to_state (view, tab->priv->state);
}

static void
set_view_properties_according_to_state (XeditTab      *tab,
					XeditTabState  state)
{
	gboolean val;

	val = ((state == XEDIT_TAB_STATE_NORMAL) &&
	       (tab->priv->print_preview == NULL) &&
	       !tab->priv->not_editable);
	gtk_text_view_set_editable (GTK_TEXT_VIEW (tab->priv->view), val);

	val = ((state != XEDIT_TAB_STATE_LOADING) &&
	       (state != XEDIT_TAB_STATE_CLOSING));
	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (tab->priv->view), val);

	val = ((state != XEDIT_TAB_STATE_LOADING) &&
	       (state != XEDIT_TAB_STATE_CLOSING) &&
	       (xedit_prefs_manager_get_highlight_current_line ()));
	gtk_source_view_set_highlight_current_line (GTK_SOURCE_VIEW (tab->priv->view), val);
}

static void
xedit_tab_set_state (XeditTab      *tab,
		     XeditTabState  state)
{
	g_return_if_fail (XEDIT_IS_TAB (tab));
	g_return_if_fail ((state >= 0) && (state < XEDIT_TAB_NUM_OF_STATES));

	if (tab->priv->state == state)
		return;

	tab->priv->state = state;

	set_view_properties_according_to_state (tab, state);

	if ((state == XEDIT_TAB_STATE_LOADING_ERROR) || /* FIXME: add other states if needed */
	    (state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW))
	{
		gtk_widget_hide (tab->priv->view_scrolled_window);
	}
	else
	{
		if (tab->priv->print_preview == NULL)
			gtk_widget_show (tab->priv->view_scrolled_window);
	}

	set_cursor_according_to_state (GTK_TEXT_VIEW (tab->priv->view),
				       state);

	g_object_notify (G_OBJECT (tab), "state");		
}

static void 
document_uri_notify_handler (XeditDocument *document,
			     GParamSpec    *pspec,
			     XeditTab      *tab)
{
	xedit_debug (DEBUG_TAB);
	
	/* Notify the change in the URI */
	g_object_notify (G_OBJECT (tab), "name");
}

static void 
document_shortname_notify_handler (XeditDocument *document,
				   GParamSpec    *pspec,
				   XeditTab      *tab)
{
	xedit_debug (DEBUG_TAB);
	
	/* Notify the change in the shortname */
	g_object_notify (G_OBJECT (tab), "name");
}

static void
document_modified_changed (GtkTextBuffer *document,
			   XeditTab      *tab)
{
	g_object_notify (G_OBJECT (tab), "name");
}

static void
set_message_area (XeditTab  *tab,
		  GtkWidget *message_area)
{
	if (tab->priv->message_area == message_area)
		return;

	if (tab->priv->message_area != NULL)
		gtk_widget_destroy (tab->priv->message_area);

	tab->priv->message_area = message_area;

	if (message_area == NULL)
		return;

	gtk_box_pack_start (GTK_BOX (tab),
			    tab->priv->message_area,
			    FALSE,
			    FALSE,
			    0);		

	g_object_add_weak_pointer (G_OBJECT (tab->priv->message_area), 
				   (gpointer *)&tab->priv->message_area);
}

static void
remove_tab (XeditTab *tab)
{
	XeditNotebook *notebook;

	notebook = XEDIT_NOTEBOOK (gtk_widget_get_parent (GTK_WIDGET (tab)));

	xedit_notebook_remove_tab (notebook, tab);
}

static void 
io_loading_error_message_area_response (GtkWidget        *message_area,
					gint              response_id,
					XeditTab         *tab)
{
	XeditDocument *doc;
	XeditView *view;
	gchar *uri;
	const XeditEncoding *encoding;

	doc = xedit_tab_get_document (tab);
	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));

	view = xedit_tab_get_view (tab);
	g_return_if_fail (XEDIT_IS_VIEW (view));

	uri = xedit_document_get_uri (doc);
	g_return_if_fail (uri != NULL);

	switch (response_id)
	{
		case GTK_RESPONSE_OK:
			encoding = xedit_conversion_error_message_area_get_encoding (
					GTK_WIDGET (message_area));

			if (encoding != NULL)
			{
				tab->priv->tmp_encoding = encoding;
			}

			set_message_area (tab, NULL);
			xedit_tab_set_state (tab, XEDIT_TAB_STATE_LOADING);

			g_return_if_fail (tab->priv->auto_save_timeout <= 0);

			xedit_document_load (doc,
					     uri,
					     tab->priv->tmp_encoding,
					     tab->priv->tmp_line_pos,
					     FALSE);
			break;
		case GTK_RESPONSE_YES:
			/* This means that we want to edit the document anyway */
			set_message_area (tab, NULL);
			_xedit_document_set_readonly (doc, FALSE);
			break;
		case GTK_RESPONSE_NO:
			/* We don't want to edit the document just show it */
			set_message_area (tab, NULL);
			break;
		default:
			_xedit_recent_remove (XEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), uri);

			remove_tab (tab);
			break;
	}

	g_free (uri);
}

static void 
file_already_open_warning_message_area_response (GtkWidget   *message_area,
						 gint         response_id,
						 XeditTab    *tab)
{
	XeditView *view;
	
	view = xedit_tab_get_view (tab);
	
	if (response_id == GTK_RESPONSE_YES)
	{
		tab->priv->not_editable = FALSE;
		
		gtk_text_view_set_editable (GTK_TEXT_VIEW (view),
					    TRUE);
	}

	gtk_widget_destroy (message_area);

	gtk_widget_grab_focus (GTK_WIDGET (view));	
}

static void
load_cancelled (GtkWidget        *area,
                gint              response_id,
                XeditTab         *tab)
{
	g_return_if_fail (XEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area));
	
	g_object_ref (tab);
	xedit_document_load_cancel (xedit_tab_get_document (tab));
	g_object_unref (tab);	
}

static void 
unrecoverable_reverting_error_message_area_response (GtkWidget        *message_area,
						     gint              response_id,
						     XeditTab         *tab)
{
	XeditView *view;
	
	xedit_tab_set_state (tab,
			     XEDIT_TAB_STATE_NORMAL);

	set_message_area (tab, NULL);

	view = xedit_tab_get_view (tab);

	gtk_widget_grab_focus (GTK_WIDGET (view));
	
	install_auto_save_timeout_if_needed (tab);	
}

#define MAX_MSG_LENGTH 100

static void
show_loading_message_area (XeditTab *tab)
{
	GtkWidget *area;
	XeditDocument *doc = NULL;
	gchar *name;
	gchar *dirname = NULL;
	gchar *msg = NULL;
	gchar *name_markup;
	gchar *dirname_markup;
	gint len;

	if (tab->priv->message_area != NULL)
		return;

	xedit_debug (DEBUG_TAB);
		
	doc = xedit_tab_get_document (tab);
	g_return_if_fail (doc != NULL);

	name = xedit_document_get_short_name_for_display (doc);
	len = g_utf8_strlen (name, -1);

	/* if the name is awfully long, truncate it and be done with it,
	 * otherwise also show the directory (ellipsized if needed)
	 */
	if (len > MAX_MSG_LENGTH)
	{
		gchar *str;

		str = xedit_utils_str_middle_truncate (name, MAX_MSG_LENGTH);
		g_free (name);
		name = str;
	}
	else
	{
		GFile *file;

		file = xedit_document_get_location (doc);
		if (file != NULL)
		{
			gchar *str;

			str = xedit_utils_location_get_dirname_for_display (file);
			g_object_unref (file);

			/* use the remaining space for the dir, but use a min of 20 chars
			 * so that we do not end up with a dirname like "(a...b)".
			 * This means that in the worst case when the filename is long 99
			 * we have a title long 99 + 20, but I think it's a rare enough
			 * case to be acceptable. It's justa darn title afterall :)
			 */
			dirname = xedit_utils_str_middle_truncate (str, 
								   MAX (20, MAX_MSG_LENGTH - len));
			g_free (str);
		}
	}

	name_markup = g_markup_printf_escaped ("<b>%s</b>", name);

	if (tab->priv->state == XEDIT_TAB_STATE_REVERTING)
	{
		if (dirname != NULL)
		{
			dirname_markup = g_markup_printf_escaped ("<b>%s</b>", dirname);

			/* Translators: the first %s is a file name (e.g. test.txt) the second one
			   is a directory (e.g. ssh://master.gnome.org/home/users/paolo) */
			msg = g_strdup_printf (_("Reverting %s from %s"),
					       name_markup,
					       dirname_markup);
			g_free (dirname_markup);
		}
		else
		{
			msg = g_strdup_printf (_("Reverting %s"), 
					       name_markup);
		}
		
		area = xedit_progress_message_area_new (GTK_STOCK_REVERT_TO_SAVED,
							msg,
							TRUE);
	}
	else
	{
		if (dirname != NULL)
		{
			dirname_markup = g_markup_printf_escaped ("<b>%s</b>", dirname);

			/* Translators: the first %s is a file name (e.g. test.txt) the second one
			   is a directory (e.g. ssh://master.gnome.org/home/users/paolo) */
			msg = g_strdup_printf (_("Loading %s from %s"),
					       name_markup,
					       dirname_markup);
			g_free (dirname_markup);
		}
		else
		{
			msg = g_strdup_printf (_("Loading %s"), 
					       name_markup);
		}

		area = xedit_progress_message_area_new (GTK_STOCK_OPEN,
							msg,
							TRUE);
	}

	g_signal_connect (area,
			  "response",
			  G_CALLBACK (load_cancelled),
			  tab);
						 
	gtk_widget_show (area);

	set_message_area (tab, area);

	g_free (msg);
	g_free (name);
	g_free (name_markup);
	g_free (dirname);
}

static void
show_saving_message_area (XeditTab *tab)
{
	GtkWidget *area;
	XeditDocument *doc = NULL;
	gchar *short_name;
	gchar *from;
	gchar *to = NULL;
	gchar *from_markup;
	gchar *to_markup;
	gchar *msg = NULL;
	gint len;

	g_return_if_fail (tab->priv->tmp_save_uri != NULL);
	
	if (tab->priv->message_area != NULL)
		return;
	
	xedit_debug (DEBUG_TAB);
		
	doc = xedit_tab_get_document (tab);
	g_return_if_fail (doc != NULL);

	short_name = xedit_document_get_short_name_for_display (doc);

	len = g_utf8_strlen (short_name, -1);

	/* if the name is awfully long, truncate it and be done with it,
	 * otherwise also show the directory (ellipsized if needed)
	 */
	if (len > MAX_MSG_LENGTH)
	{
		from = xedit_utils_str_middle_truncate (short_name, 
							MAX_MSG_LENGTH);
		g_free (short_name);
	}
	else
	{
		gchar *str;

		from = short_name;

		to = xedit_utils_uri_for_display (tab->priv->tmp_save_uri);

		str = xedit_utils_str_middle_truncate (to, 
						       MAX (20, MAX_MSG_LENGTH - len));
		g_free (to);
			
		to = str;
	}

	from_markup = g_markup_printf_escaped ("<b>%s</b>", from);

	if (to != NULL)
	{
		to_markup = g_markup_printf_escaped ("<b>%s</b>", to);

		/* Translators: the first %s is a file name (e.g. test.txt) the second one
		   is a directory (e.g. ssh://master.gnome.org/home/users/paolo) */
		msg = g_strdup_printf (_("Saving %s to %s"),
				       from_markup,
				       to_markup);
		g_free (to_markup);
	}
	else
	{
		msg = g_strdup_printf (_("Saving %s"), from_markup);
	}

	area = xedit_progress_message_area_new (GTK_STOCK_SAVE,
						msg,
						FALSE);

	gtk_widget_show (area);

	set_message_area (tab, area);

	g_free (msg);
	g_free (to);
	g_free (from);
	g_free (from_markup);
}

static void
message_area_set_progress (XeditTab *tab,
			   goffset   size,
			   goffset   total_size)
{
	if (tab->priv->message_area == NULL)
		return;

	xedit_debug_message (DEBUG_TAB, "%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, size, total_size);

	g_return_if_fail (XEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area));
	
	if (total_size == 0)
	{
		if (size != 0)
			xedit_progress_message_area_pulse (
					XEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area));	
		else
			xedit_progress_message_area_set_fraction (
				XEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area),
				0);
	}
	else
	{
		gdouble frac;

		frac = (gdouble)size / (gdouble)total_size;

		xedit_progress_message_area_set_fraction (
				XEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area),
				frac);		
	}
}

static void
document_loading (XeditDocument *document,
		  goffset        size,
		  goffset        total_size,
		  XeditTab      *tab)
{
	gdouble et;
	gdouble total_time;

	g_return_if_fail ((tab->priv->state == XEDIT_TAB_STATE_LOADING) ||
		 	  (tab->priv->state == XEDIT_TAB_STATE_REVERTING));

	xedit_debug_message (DEBUG_TAB, "%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, size, total_size);

	if (tab->priv->timer == NULL)
	{
		g_return_if_fail (tab->priv->times_called == 0);
		tab->priv->timer = g_timer_new ();
	}

	et = g_timer_elapsed (tab->priv->timer, NULL);

	/* et : total_time = size : total_size */
	total_time = (et * total_size) / size;

	if ((total_time - et) > 3.0)
	{
		show_loading_message_area (tab);
	}

	message_area_set_progress (tab, size, total_size);
}

static gboolean
remove_tab_idle (XeditTab *tab)
{
	remove_tab (tab);

	return FALSE;
}

static void
document_loaded (XeditDocument *document,
		 const GError  *error,
		 XeditTab      *tab)
{
	GtkWidget *emsg;
	GFile *location;
	gchar *uri;
	const XeditEncoding *encoding;

	g_return_if_fail ((tab->priv->state == XEDIT_TAB_STATE_LOADING) ||
			  (tab->priv->state == XEDIT_TAB_STATE_REVERTING));
	g_return_if_fail (tab->priv->auto_save_timeout <= 0);

	if (tab->priv->timer != NULL)
	{
		g_timer_destroy (tab->priv->timer);
		tab->priv->timer = NULL;
	}
	tab->priv->times_called = 0;

	set_message_area (tab, NULL);

	location = xedit_document_get_location (document);
	uri = xedit_document_get_uri (document);

	/* if the error is CONVERSION FALLBACK don't treat it as a normal error */
	if (error != NULL &&
	    (error->domain != XEDIT_DOCUMENT_ERROR || error->code != XEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK))
	{
		if (tab->priv->state == XEDIT_TAB_STATE_LOADING)
			xedit_tab_set_state (tab, XEDIT_TAB_STATE_LOADING_ERROR);
		else
			xedit_tab_set_state (tab, XEDIT_TAB_STATE_REVERTING_ERROR);

		encoding = xedit_document_get_encoding (document);

		if (error->domain == G_IO_ERROR &&
		    error->code == G_IO_ERROR_CANCELLED)
		{
			/* remove the tab, but in an idle handler, since
			 * we are in the handler of doc loaded and we 
			 * don't want doc and tab to be finalized now.
			 */
			g_idle_add ((GSourceFunc) remove_tab_idle, tab);

			goto end;
		}
		else
		{
			_xedit_recent_remove (XEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), uri);

			if (tab->priv->state == XEDIT_TAB_STATE_LOADING_ERROR)
			{
				emsg = xedit_io_loading_error_message_area_new (uri,
										tab->priv->tmp_encoding,
										error);
				g_signal_connect (emsg,
						  "response",
						  G_CALLBACK (io_loading_error_message_area_response),
						  tab);
			}
			else
			{
				g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_REVERTING_ERROR);
				
				emsg = xedit_unrecoverable_reverting_error_message_area_new (uri,
											     error);

				g_signal_connect (emsg,
						  "response",
						  G_CALLBACK (unrecoverable_reverting_error_message_area_response),
						  tab);
			}

			set_message_area (tab, emsg);
		}

		gtk_info_bar_set_default_response (GTK_INFO_BAR (emsg),
						   GTK_RESPONSE_CANCEL);

		gtk_widget_show (emsg);

		g_object_unref (location);
		g_free (uri);

		return;
	}
	else
	{
		gchar *mime;
		GList *all_documents;
		GList *l;

		g_return_if_fail (uri != NULL);

		mime = xedit_document_get_mime_type (document);
		_xedit_recent_add (XEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))),
				   uri,
				   mime);
		g_free (mime);

		if (error &&
		    error->domain == XEDIT_DOCUMENT_ERROR &&
		    error->code == XEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK)
		{
			GtkWidget *emsg;

			_xedit_document_set_readonly (document, TRUE);

			emsg = xedit_io_loading_error_message_area_new (uri,
									tab->priv->tmp_encoding,
									error);

			set_message_area (tab, emsg);

			g_signal_connect (emsg,
					  "response",
					  G_CALLBACK (io_loading_error_message_area_response),
					  tab);

			gtk_info_bar_set_default_response (GTK_INFO_BAR (emsg),
							   GTK_RESPONSE_CANCEL);

			gtk_widget_show (emsg);
		}

		/* Scroll to the cursor when the document is loaded */
		xedit_view_scroll_to_cursor (XEDIT_VIEW (tab->priv->view));

		all_documents = xedit_app_get_documents (xedit_app_get_default ());

		for (l = all_documents; l != NULL; l = g_list_next (l))
		{
			XeditDocument *d = XEDIT_DOCUMENT (l->data);
			
			if (d != document)
			{
				GFile *loc;

				loc = xedit_document_get_location (d);

				if ((loc != NULL) &&
			    	    g_file_equal (location, loc))
			    	{
			    		GtkWidget *w;
			    		XeditView *view;

			    		view = xedit_tab_get_view (tab);

			    		tab->priv->not_editable = TRUE;

			    		w = xedit_file_already_open_warning_message_area_new (uri);

					set_message_area (tab, w);

					gtk_info_bar_set_default_response (GTK_INFO_BAR (w),
									   GTK_RESPONSE_CANCEL);

					gtk_widget_show (w);

					g_signal_connect (w,
							  "response",
							  G_CALLBACK (file_already_open_warning_message_area_response),
							  tab);

			    		g_object_unref (loc);
			    		break;
			    	}
			    	
			    	if (loc != NULL)
					g_object_unref (loc);
			}
		}

		g_list_free (all_documents);

		xedit_tab_set_state (tab, XEDIT_TAB_STATE_NORMAL);
		
		install_auto_save_timeout_if_needed (tab);

		tab->priv->ask_if_externally_modified = TRUE;
	}

 end:
	g_object_unref (location);
	g_free (uri);

	tab->priv->tmp_line_pos = 0;
	tab->priv->tmp_encoding = NULL;
}

static void
document_saving (XeditDocument    *document,
		 goffset  size,
		 goffset  total_size,
		 XeditTab         *tab)
{
	gdouble et;
	gdouble total_time;

	g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_SAVING);

	xedit_debug_message (DEBUG_TAB, "%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, size, total_size);


	if (tab->priv->timer == NULL)
	{
		g_return_if_fail (tab->priv->times_called == 0);
		tab->priv->timer = g_timer_new ();
	}

	et = g_timer_elapsed (tab->priv->timer, NULL);

	/* et : total_time = size : total_size */
	total_time = (et * total_size)/size;

	if ((total_time - et) > 3.0)
	{
		show_saving_message_area (tab);
	}

	message_area_set_progress (tab, size, total_size);

	tab->priv->times_called++;
}

static void
end_saving (XeditTab *tab)
{
	/* Reset tmp data for saving */
	g_free (tab->priv->tmp_save_uri);
	tab->priv->tmp_save_uri = NULL;
	tab->priv->tmp_encoding = NULL;
	
	install_auto_save_timeout_if_needed (tab);
}

static void 
unrecoverable_saving_error_message_area_response (GtkWidget        *message_area,
						  gint              response_id,
						  XeditTab         *tab)
{
	XeditView *view;
	
	if (tab->priv->print_preview != NULL)
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW);
	else
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_NORMAL);

	end_saving (tab);
	
	set_message_area (tab, NULL);

	view = xedit_tab_get_view (tab);

	gtk_widget_grab_focus (GTK_WIDGET (view));	
}

static void 
no_backup_error_message_area_response (GtkWidget        *message_area,
				       gint              response_id,
				       XeditTab         *tab)
{
	if (response_id == GTK_RESPONSE_YES)
	{
		XeditDocument *doc;

		doc = xedit_tab_get_document (tab);
		g_return_if_fail (XEDIT_IS_DOCUMENT (doc));

		set_message_area (tab, NULL);

		g_return_if_fail (tab->priv->tmp_save_uri != NULL);
		g_return_if_fail (tab->priv->tmp_encoding != NULL);

		xedit_tab_set_state (tab, XEDIT_TAB_STATE_SAVING);

		/* don't bug the user again with this... */
		tab->priv->save_flags |= XEDIT_DOCUMENT_SAVE_IGNORE_BACKUP;

		g_return_if_fail (tab->priv->auto_save_timeout <= 0);
		
		/* Force saving */
		xedit_document_save (doc, tab->priv->save_flags);
	}
	else
	{
		unrecoverable_saving_error_message_area_response (message_area,
								  response_id,
								  tab);
	}
}

static void
externally_modified_error_message_area_response (GtkWidget        *message_area,
						 gint              response_id,
						 XeditTab         *tab)
{
	if (response_id == GTK_RESPONSE_YES)
	{
		XeditDocument *doc;

		doc = xedit_tab_get_document (tab);
		g_return_if_fail (XEDIT_IS_DOCUMENT (doc));
		
		set_message_area (tab, NULL);

		g_return_if_fail (tab->priv->tmp_save_uri != NULL);
		g_return_if_fail (tab->priv->tmp_encoding != NULL);

		xedit_tab_set_state (tab, XEDIT_TAB_STATE_SAVING);

		g_return_if_fail (tab->priv->auto_save_timeout <= 0);
		
		/* ignore mtime should not be persisted in save flags across saves */

		/* Force saving */
		xedit_document_save (doc, tab->priv->save_flags | XEDIT_DOCUMENT_SAVE_IGNORE_MTIME);
	}
	else
	{		
		unrecoverable_saving_error_message_area_response (message_area,
								  response_id,
								  tab);
	}
}

static void 
recoverable_saving_error_message_area_response (GtkWidget        *message_area,
						gint              response_id,
						XeditTab         *tab)
{
	XeditDocument *doc;

	doc = xedit_tab_get_document (tab);
	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));
	
	if (response_id == GTK_RESPONSE_OK)
	{
		const XeditEncoding *encoding;
		
		encoding = xedit_conversion_error_message_area_get_encoding (
									GTK_WIDGET (message_area));

		g_return_if_fail (encoding != NULL);

		set_message_area (tab, NULL);

		g_return_if_fail (tab->priv->tmp_save_uri != NULL);
				
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_SAVING);
			
		tab->priv->tmp_encoding = encoding;

		xedit_debug_message (DEBUG_TAB, "Force saving with URI '%s'", tab->priv->tmp_save_uri);
			 
		g_return_if_fail (tab->priv->auto_save_timeout <= 0);
		
		xedit_document_save_as (doc,
					tab->priv->tmp_save_uri,
					tab->priv->tmp_encoding,
					tab->priv->save_flags);
	}
	else
	{		
		unrecoverable_saving_error_message_area_response (message_area,
								  response_id,
								  tab);
	}
}

static void
document_saved (XeditDocument *document,
		const GError  *error,
		XeditTab      *tab)
{
	GtkWidget *emsg;

	g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_SAVING);

	g_return_if_fail (tab->priv->tmp_save_uri != NULL);
	g_return_if_fail (tab->priv->tmp_encoding != NULL);	
	g_return_if_fail (tab->priv->auto_save_timeout <= 0);
	
	g_timer_destroy (tab->priv->timer);
	tab->priv->timer = NULL;
	tab->priv->times_called = 0;
	
	set_message_area (tab, NULL);
	
	if (error != NULL)
	{
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_SAVING_ERROR);
		
		if (error->domain == XEDIT_DOCUMENT_ERROR &&
		    error->code == XEDIT_DOCUMENT_ERROR_EXTERNALLY_MODIFIED)
		{
			/* This error is recoverable */
			emsg = xedit_externally_modified_saving_error_message_area_new (
							tab->priv->tmp_save_uri, 
							error);
			g_return_if_fail (emsg != NULL);

			set_message_area (tab, emsg);

			g_signal_connect (emsg,
					  "response",
					  G_CALLBACK (externally_modified_error_message_area_response),
					  tab);
		}
		else if ((error->domain == XEDIT_DOCUMENT_ERROR &&
			  error->code == XEDIT_DOCUMENT_ERROR_CANT_CREATE_BACKUP) ||
		         (error->domain == G_IO_ERROR &&
			  error->code == G_IO_ERROR_CANT_CREATE_BACKUP))
		{
			/* This error is recoverable */
			emsg = xedit_no_backup_saving_error_message_area_new (
							tab->priv->tmp_save_uri, 
							error);
			g_return_if_fail (emsg != NULL);

			set_message_area (tab, emsg);

			g_signal_connect (emsg,
					  "response",
					  G_CALLBACK (no_backup_error_message_area_response),
					  tab);
		}
		else if (error->domain == XEDIT_DOCUMENT_ERROR ||
			 (error->domain == G_IO_ERROR &&
			  error->code != G_IO_ERROR_INVALID_DATA &&
			  error->code != G_IO_ERROR_PARTIAL_INPUT))
		{
			/* These errors are _NOT_ recoverable */
			_xedit_recent_remove  (XEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))),
					       tab->priv->tmp_save_uri);

			emsg = xedit_unrecoverable_saving_error_message_area_new (tab->priv->tmp_save_uri, 
								  error);
			g_return_if_fail (emsg != NULL);
	
			set_message_area (tab, emsg);

			g_signal_connect (emsg,
					  "response",
					  G_CALLBACK (unrecoverable_saving_error_message_area_response),
					  tab);
		}
		else
		{
			/* This error is recoverable */
			g_return_if_fail (error->domain == G_CONVERT_ERROR ||
			                  error->domain == G_IO_ERROR);

			emsg = xedit_conversion_error_while_saving_message_area_new (
									tab->priv->tmp_save_uri,
									tab->priv->tmp_encoding,
									error);

			set_message_area (tab, emsg);

			g_signal_connect (emsg,
					  "response",
					  G_CALLBACK (recoverable_saving_error_message_area_response),
					  tab);
		}

		gtk_info_bar_set_default_response (GTK_INFO_BAR (emsg),
						   GTK_RESPONSE_CANCEL);

		gtk_widget_show (emsg);
	}
	else
	{
		gchar *mime = xedit_document_get_mime_type (document);

		_xedit_recent_add (XEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))),
				   tab->priv->tmp_save_uri,
				   mime);
		g_free (mime);

		if (tab->priv->print_preview != NULL)
			xedit_tab_set_state (tab, XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW);
		else
			xedit_tab_set_state (tab, XEDIT_TAB_STATE_NORMAL);

		tab->priv->ask_if_externally_modified = TRUE;
			
		end_saving (tab);
	}
}

static void 
externally_modified_notification_message_area_response (GtkWidget        *message_area,
							gint              response_id,
							XeditTab         *tab)
{
	XeditView *view;

	set_message_area (tab, NULL);
	view = xedit_tab_get_view (tab);

	if (response_id == GTK_RESPONSE_OK)
	{
		_xedit_tab_revert (tab);
	}
	else
	{
		tab->priv->ask_if_externally_modified = FALSE;

		/* go back to normal state */
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_NORMAL);
	}

	gtk_widget_grab_focus (GTK_WIDGET (view));
}

static void
display_externally_modified_notification (XeditTab *tab)
{
	GtkWidget *message_area;
	XeditDocument *doc;
	gchar *uri;
	gboolean document_modified;

	doc = xedit_tab_get_document (tab);
	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));

	/* uri cannot be NULL, we're here because
	 * the file we're editing changed on disk */
	uri = xedit_document_get_uri (doc);
	g_return_if_fail (uri != NULL);

	document_modified = gtk_text_buffer_get_modified (GTK_TEXT_BUFFER(doc));
	message_area = xedit_externally_modified_message_area_new (uri, document_modified);
	g_free (uri);

	tab->priv->message_area = NULL;
	set_message_area (tab, message_area);
	gtk_widget_show (message_area);

	g_signal_connect (message_area,
			  "response",
			  G_CALLBACK (externally_modified_notification_message_area_response),
			  tab);
}

static gboolean
view_focused_in (GtkWidget     *widget,
                 GdkEventFocus *event,
                 XeditTab      *tab)
{
	XeditDocument *doc;

	g_return_val_if_fail (XEDIT_IS_TAB (tab), FALSE);

	/* we try to detect file changes only in the normal state */
	if (tab->priv->state != XEDIT_TAB_STATE_NORMAL)
	{
		return FALSE;
	}

	/* we already asked, don't bug the user again */
	if (!tab->priv->ask_if_externally_modified)
	{
		return FALSE;
	}

	doc = xedit_tab_get_document (tab);

	/* If file was never saved or is remote we do not check */
	if (!xedit_document_is_local (doc))
	{
		return FALSE;
	}

	if (_xedit_document_check_externally_modified (doc))
	{
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION);

		display_externally_modified_notification (tab);

		return FALSE;
	}

	return FALSE;
}

static GMountOperation *
tab_mount_operation_factory (XeditDocument *doc,
			     gpointer userdata)
{
	XeditTab *tab = XEDIT_TAB (userdata);
	GtkWidget *window;

	window = gtk_widget_get_toplevel (GTK_WIDGET (tab));
	return gtk_mount_operation_new (GTK_WINDOW (window));
}

static void
xedit_tab_init (XeditTab *tab)
{
	GtkWidget *sw;
	XeditDocument *doc;

	tab->priv = XEDIT_TAB_GET_PRIVATE (tab);

	tab->priv->state = XEDIT_TAB_STATE_NORMAL;

	tab->priv->not_editable = FALSE;

	tab->priv->save_flags = 0;

	tab->priv->ask_if_externally_modified = TRUE;

#if GTK_CHECK_VERSION (3, 0, 0)
	gtk_orientable_set_orientation (GTK_ORIENTABLE (tab),
	                                GTK_ORIENTATION_VERTICAL);
#endif
	
	/* Create the scrolled window */
	sw = gtk_scrolled_window_new (NULL, NULL);
	tab->priv->view_scrolled_window = sw;

	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);

	/* Manage auto save data */
	tab->priv->auto_save = xedit_prefs_manager_get_auto_save ();
	tab->priv->auto_save = (tab->priv->auto_save != FALSE);

	tab->priv->auto_save_interval = xedit_prefs_manager_get_auto_save_interval ();
	if (tab->priv->auto_save_interval <= 0)
		tab->priv->auto_save_interval = GPM_DEFAULT_AUTO_SAVE_INTERVAL;

	/* Create the view */
	doc = xedit_document_new ();
	g_object_set_data (G_OBJECT (doc), XEDIT_TAB_KEY, tab);

	_xedit_document_set_mount_operation_factory (doc,
						     tab_mount_operation_factory,
						     tab);

	tab->priv->view = xedit_view_new (doc);
	g_object_unref (doc);
	gtk_widget_show (tab->priv->view);
	g_object_set_data (G_OBJECT (tab->priv->view), XEDIT_TAB_KEY, tab);

	gtk_box_pack_end (GTK_BOX (tab), sw, TRUE, TRUE, 0);
	gtk_container_add (GTK_CONTAINER (sw), tab->priv->view);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
					     GTK_SHADOW_IN);	
	gtk_widget_show (sw);

	g_signal_connect (doc,
			  "notify::uri",
			  G_CALLBACK (document_uri_notify_handler),
			  tab);
	g_signal_connect (doc,
			  "notify::shortname",
			  G_CALLBACK (document_shortname_notify_handler),
			  tab);
	g_signal_connect (doc,
			  "modified_changed",
			  G_CALLBACK (document_modified_changed),
			  tab);
	g_signal_connect (doc,
			  "loading",
			  G_CALLBACK (document_loading),
			  tab);
	g_signal_connect (doc,
			  "loaded",
			  G_CALLBACK (document_loaded),
			  tab);
	g_signal_connect (doc,
			  "saving",
			  G_CALLBACK (document_saving),
			  tab);
	g_signal_connect (doc,
			  "saved",
			  G_CALLBACK (document_saved),
			  tab);

	g_signal_connect_after (tab->priv->view,
				"focus-in-event",
				G_CALLBACK (view_focused_in),
				tab);

	g_signal_connect_after (tab->priv->view,
				"realize",
				G_CALLBACK (view_realized),
				tab);
}

GtkWidget *
_xedit_tab_new (void)
{
	return GTK_WIDGET (g_object_new (XEDIT_TYPE_TAB, NULL));
}

/* Whether create is TRUE, creates a new empty document if location does 
   not refer to an existing file */
GtkWidget *
_xedit_tab_new_from_uri (const gchar         *uri,
			 const XeditEncoding *encoding,
			 gint                 line_pos,			
			 gboolean             create)
{
	XeditTab *tab;

	g_return_val_if_fail (uri != NULL, NULL);

	tab = XEDIT_TAB (_xedit_tab_new ());

	_xedit_tab_load (tab,
			 uri,
			 encoding,
			 line_pos,
			 create);

	return GTK_WIDGET (tab);
}		

/**
 * xedit_tab_get_view:
 * @tab: a #XeditTab
 *
 * Gets the #XeditView inside @tab.
 *
 * Returns: the #XeditView inside @tab
 */
XeditView *
xedit_tab_get_view (XeditTab *tab)
{
	return XEDIT_VIEW (tab->priv->view);
}

/**
 * xedit_tab_get_document:
 * @tab: a #XeditTab
 *
 * Gets the #XeditDocument associated to @tab.
 *
 * Returns: the #XeditDocument associated to @tab
 */
XeditDocument *
xedit_tab_get_document (XeditTab *tab)
{
	return XEDIT_DOCUMENT (gtk_text_view_get_buffer (
					GTK_TEXT_VIEW (tab->priv->view)));
}

#define MAX_DOC_NAME_LENGTH 40

gchar *
_xedit_tab_get_name (XeditTab *tab)
{
	XeditDocument *doc;
	gchar *name;
	gchar *docname;
	gchar *tab_name;

	g_return_val_if_fail (XEDIT_IS_TAB (tab), NULL);

	doc = xedit_tab_get_document (tab);

	name = xedit_document_get_short_name_for_display (doc);

	/* Truncate the name so it doesn't get insanely wide. */
	docname = xedit_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH);

	if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)))
	{
		tab_name = g_strdup_printf ("*%s", docname);
	} 
	else 
	{
 #if 0		
		if (xedit_document_get_readonly (doc)) 
		{
			tab_name = g_strdup_printf ("%s [%s]", docname, 
						/*Read only*/ _("RO"));
		} 
		else 
		{
			tab_name = g_strdup_printf ("%s", docname);
		}
#endif
		tab_name = g_strdup (docname);
	}
	
	g_free (docname);
	g_free (name);

	return tab_name;
}

gchar *
_xedit_tab_get_tooltips	(XeditTab *tab)
{
	XeditDocument *doc;
	gchar *tip;
	gchar *uri;
	gchar *ruri;
	gchar *ruri_markup;

	g_return_val_if_fail (XEDIT_IS_TAB (tab), NULL);

	doc = xedit_tab_get_document (tab);

	uri = xedit_document_get_uri_for_display (doc);
	g_return_val_if_fail (uri != NULL, NULL);

	ruri = 	xedit_utils_replace_home_dir_with_tilde (uri);
	g_free (uri);

	ruri_markup = g_markup_printf_escaped ("<i>%s</i>", ruri);

	switch (tab->priv->state)
	{
		gchar *content_type;
		gchar *mime_type;
		gchar *content_description;
		gchar *content_full_description; 
		gchar *encoding;
		const XeditEncoding *enc;

		case XEDIT_TAB_STATE_LOADING_ERROR:
			tip = g_strdup_printf (_("Error opening file %s"),
					       ruri_markup);
			break;

		case XEDIT_TAB_STATE_REVERTING_ERROR:
			tip = g_strdup_printf (_("Error reverting file %s"),
					       ruri_markup);
			break;			

		case XEDIT_TAB_STATE_SAVING_ERROR:
			tip =  g_strdup_printf (_("Error saving file %s"),
						ruri_markup);
			break;			
		default:
			content_type = xedit_document_get_content_type (doc);
			mime_type = xedit_document_get_mime_type (doc);
			content_description = g_content_type_get_description (content_type);

			if (content_description == NULL)
				content_full_description = g_strdup (mime_type);
			else
				content_full_description = g_strdup_printf ("%s (%s)", 
						content_description, mime_type);

			g_free (content_type);
			g_free (mime_type);
			g_free (content_description);

			enc = xedit_document_get_encoding (doc);

			if (enc == NULL)
				encoding = g_strdup (_("Unicode (UTF-8)"));
			else
				encoding = xedit_encoding_to_string (enc);

			tip =  g_markup_printf_escaped ("<b>%s</b> %s\n\n"
						        "<b>%s</b> %s\n"
						        "<b>%s</b> %s",
						        _("Name:"), ruri,
						        _("MIME Type:"), content_full_description,
						        _("Encoding:"), encoding);

			g_free (encoding);
			g_free (content_full_description);

			break;
	}

	g_free (ruri);	
	g_free (ruri_markup);
	
	return tip;
}

static GdkPixbuf *
resize_icon (GdkPixbuf *pixbuf,
	     gint       size)
{
	gint width, height;

	width = gdk_pixbuf_get_width (pixbuf); 
	height = gdk_pixbuf_get_height (pixbuf);

	/* if the icon is larger than the nominal size, scale down */
	if (MAX (width, height) > size) 
	{
		GdkPixbuf *scaled_pixbuf;
		
		if (width > height) 
		{
			height = height * size / width;
			width = size;
		} 
		else 
		{
			width = width * size / height;
			height = size;
		}
		
		scaled_pixbuf = gdk_pixbuf_scale_simple	(pixbuf, 
							 width, 
							 height, 
							 GDK_INTERP_BILINEAR);
		g_object_unref (pixbuf);
		pixbuf = scaled_pixbuf;
	}

	return pixbuf;
}

static GdkPixbuf *
get_stock_icon (GtkIconTheme *theme, 
		const gchar  *stock,
		gint          size)
{
	GdkPixbuf *pixbuf;
	
	pixbuf = gtk_icon_theme_load_icon (theme, stock, size, 0, NULL);
	if (pixbuf == NULL)
		return NULL;
		
	return resize_icon (pixbuf, size);
}

static GdkPixbuf *
get_icon (GtkIconTheme *theme, 
	  GFile        *location,
	  gint          size)
{
	GdkPixbuf *pixbuf;
	GtkIconInfo *icon_info;
	GFileInfo *info;
	GIcon *gicon;

	if (location == NULL)
		return get_stock_icon (theme, GTK_STOCK_FILE, size);

	/* FIXME: Doing a sync stat is bad, this should be fixed */
	info = g_file_query_info (location, 
	                          G_FILE_ATTRIBUTE_STANDARD_ICON, 
	                          G_FILE_QUERY_INFO_NONE, 
	                          NULL, 
	                          NULL);
	if (info == NULL)
		return get_stock_icon (theme, GTK_STOCK_FILE, size);

	gicon = g_file_info_get_icon (info);

	if (gicon == NULL)
	{
		g_object_unref (info);
		return get_stock_icon (theme, GTK_STOCK_FILE, size);
	}

	icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, size, 0);
	g_object_unref (info);	
	
	if (icon_info == NULL)
		return get_stock_icon (theme, GTK_STOCK_FILE, size);
	
	pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
	gtk_icon_info_free (icon_info);
	
	if (pixbuf == NULL)
		return get_stock_icon (theme, GTK_STOCK_FILE, size);
		
	return resize_icon (pixbuf, size);
}

/* FIXME: add support for theme changed. I think it should be as easy as
   call g_object_notify (tab, "name") when the icon theme changes */
GdkPixbuf *
_xedit_tab_get_icon (XeditTab *tab)
{
	GdkPixbuf *pixbuf;
	GtkIconTheme *theme;
	GdkScreen *screen;
	gint icon_size;

	g_return_val_if_fail (XEDIT_IS_TAB (tab), NULL);

	screen = gtk_widget_get_screen (GTK_WIDGET (tab));

	theme = gtk_icon_theme_get_for_screen (screen);
	g_return_val_if_fail (theme != NULL, NULL);

#if GTK_CHECK_VERSION (3, 10, 0)
	gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size);
#else
	gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (tab)),
					   GTK_ICON_SIZE_MENU, 
					   NULL,
					   &icon_size);
#endif

	switch (tab->priv->state)
	{
		case XEDIT_TAB_STATE_LOADING:
			pixbuf = get_stock_icon (theme, 
						 GTK_STOCK_OPEN, 
						 icon_size);
			break;

		case XEDIT_TAB_STATE_REVERTING:
			pixbuf = get_stock_icon (theme, 
						 GTK_STOCK_REVERT_TO_SAVED, 
						 icon_size);						 
			break;

		case XEDIT_TAB_STATE_SAVING:
			pixbuf = get_stock_icon (theme, 
						 GTK_STOCK_SAVE, 
						 icon_size);
			break;

		case XEDIT_TAB_STATE_PRINTING:
			pixbuf = get_stock_icon (theme, 
						 GTK_STOCK_PRINT, 
						 icon_size);
			break;

		case XEDIT_TAB_STATE_PRINT_PREVIEWING:
		case XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW:		
			pixbuf = get_stock_icon (theme, 
						 GTK_STOCK_PRINT_PREVIEW, 
						 icon_size);
			break;

		case XEDIT_TAB_STATE_LOADING_ERROR:
		case XEDIT_TAB_STATE_REVERTING_ERROR:
		case XEDIT_TAB_STATE_SAVING_ERROR:
		case XEDIT_TAB_STATE_GENERIC_ERROR:
			pixbuf = get_stock_icon (theme, 
						 GTK_STOCK_DIALOG_ERROR, 
						 icon_size);
			break;

		case XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION:
			pixbuf = get_stock_icon (theme,
						 GTK_STOCK_DIALOG_WARNING,
						 icon_size);
			break;

		default:
		{
			GFile *location;
			XeditDocument *doc;

			doc = xedit_tab_get_document (tab);

			location = xedit_document_get_location (doc);
			pixbuf = get_icon (theme, location, icon_size);

			if (location)
				g_object_unref (location);
		}
	}

	return pixbuf;
}

/**
 * xedit_tab_get_from_document:
 * @doc: a #XeditDocument
 *
 * Gets the #XeditTab associated with @doc.
 *
 * Returns: the #XeditTab associated with @doc
 */
XeditTab *
xedit_tab_get_from_document (XeditDocument *doc)
{
	gpointer res;
	
	g_return_val_if_fail (XEDIT_IS_DOCUMENT (doc), NULL);
	
	res = g_object_get_data (G_OBJECT (doc), XEDIT_TAB_KEY);
	
	return (res != NULL) ? XEDIT_TAB (res) : NULL;
}

void
_xedit_tab_load (XeditTab            *tab,
		 const gchar         *uri,
		 const XeditEncoding *encoding,
		 gint                 line_pos,
		 gboolean             create)
{
	XeditDocument *doc;

	g_return_if_fail (XEDIT_IS_TAB (tab));
	g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_NORMAL);

	doc = xedit_tab_get_document (tab);
	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));

	xedit_tab_set_state (tab, XEDIT_TAB_STATE_LOADING);

	tab->priv->tmp_line_pos = line_pos;
	tab->priv->tmp_encoding = encoding;

	if (tab->priv->auto_save_timeout > 0)
		remove_auto_save_timeout (tab);

	xedit_document_load (doc,
			     uri,
			     encoding,
			     line_pos,
			     create);
}

void
_xedit_tab_revert (XeditTab *tab)
{
	XeditDocument *doc;
	gchar *uri;

	g_return_if_fail (XEDIT_IS_TAB (tab));
	g_return_if_fail ((tab->priv->state == XEDIT_TAB_STATE_NORMAL) ||
			  (tab->priv->state == XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION));

	if (tab->priv->state == XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)
	{
		set_message_area (tab, NULL);
	}

	doc = xedit_tab_get_document (tab);
	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));

	xedit_tab_set_state (tab, XEDIT_TAB_STATE_REVERTING);

	uri = xedit_document_get_uri (doc);
	g_return_if_fail (uri != NULL);

	tab->priv->tmp_line_pos = 0;
	tab->priv->tmp_encoding = xedit_document_get_encoding (doc);

	if (tab->priv->auto_save_timeout > 0)
		remove_auto_save_timeout (tab);

	xedit_document_load (doc,
			     uri,
			     tab->priv->tmp_encoding,
			     0,
			     FALSE);

	g_free (uri);
}

void
_xedit_tab_save (XeditTab *tab)
{
	XeditDocument *doc;
	XeditDocumentSaveFlags save_flags;

	g_return_if_fail (XEDIT_IS_TAB (tab));
	g_return_if_fail ((tab->priv->state == XEDIT_TAB_STATE_NORMAL) ||
			  (tab->priv->state == XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) ||
			  (tab->priv->state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW));
	g_return_if_fail (tab->priv->tmp_save_uri == NULL);
	g_return_if_fail (tab->priv->tmp_encoding == NULL);

	doc = xedit_tab_get_document (tab);
	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));
	g_return_if_fail (!xedit_document_is_untitled (doc));

	if (tab->priv->state == XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)
	{
		/* We already told the user about the external
		 * modification: hide the message area and set
		 * the save flag.
		 */

		set_message_area (tab, NULL);
		save_flags = tab->priv->save_flags | XEDIT_DOCUMENT_SAVE_IGNORE_MTIME;
	}
	else
	{
		save_flags = tab->priv->save_flags;
	}

	xedit_tab_set_state (tab, XEDIT_TAB_STATE_SAVING);

	/* uri used in error messages, will be freed in document_saved */
	tab->priv->tmp_save_uri = xedit_document_get_uri (doc);
	tab->priv->tmp_encoding = xedit_document_get_encoding (doc); 

	if (tab->priv->auto_save_timeout > 0)
		remove_auto_save_timeout (tab);
		
	xedit_document_save (doc, save_flags);
}

static gboolean
xedit_tab_auto_save (XeditTab *tab)
{
	XeditDocument *doc;

	xedit_debug (DEBUG_TAB);
	
	g_return_val_if_fail (tab->priv->tmp_save_uri == NULL, FALSE);
	g_return_val_if_fail (tab->priv->tmp_encoding == NULL, FALSE);
	
	doc = xedit_tab_get_document (tab);
	
	g_return_val_if_fail (!xedit_document_is_untitled (doc), FALSE);
	g_return_val_if_fail (!xedit_document_get_readonly (doc), FALSE);

	g_return_val_if_fail (tab->priv->auto_save_timeout > 0, FALSE);
	g_return_val_if_fail (tab->priv->auto_save, FALSE);
	g_return_val_if_fail (tab->priv->auto_save_interval > 0, FALSE);

	if (!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER(doc)))
	{
		xedit_debug_message (DEBUG_TAB, "Document not modified");

		return TRUE;
	}
			
	if ((tab->priv->state != XEDIT_TAB_STATE_NORMAL) &&
	    (tab->priv->state != XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW))
	{
		/* Retry after 30 seconds */
		guint timeout;

		xedit_debug_message (DEBUG_TAB, "Retry after 30 seconds");

		/* Add a new timeout */
		timeout = g_timeout_add_seconds (30,
						 (GSourceFunc) xedit_tab_auto_save,
						 tab);

		tab->priv->auto_save_timeout = timeout;

	    	/* Returns FALSE so the old timeout is "destroyed" */
		return FALSE;
	}
	
	xedit_tab_set_state (tab, XEDIT_TAB_STATE_SAVING);

	/* uri used in error messages, will be freed in document_saved */
	tab->priv->tmp_save_uri = xedit_document_get_uri (doc);
	tab->priv->tmp_encoding = xedit_document_get_encoding (doc); 

	/* Set auto_save_timeout to 0 since the timeout is going to be destroyed */
	tab->priv->auto_save_timeout = 0;

	/* Since we are autosaving, we need to preserve the backup that was produced
	   the last time the user "manually" saved the file. In the case a recoverable
	   error happens while saving, the last backup is not preserved since the user
	   expressed his willing of saving the file */
	xedit_document_save (doc, tab->priv->save_flags | XEDIT_DOCUMENT_SAVE_PRESERVE_BACKUP);
	
	xedit_debug_message (DEBUG_TAB, "Done");
	
	/* Returns FALSE so the old timeout is "destroyed" */
	return FALSE;
}

void
_xedit_tab_save_as (XeditTab                 *tab,
                    const gchar              *uri,
                    const XeditEncoding      *encoding,
                    XeditDocumentNewlineType  newline_type)
{
	XeditDocument *doc;
	XeditDocumentSaveFlags save_flags;

	g_return_if_fail (XEDIT_IS_TAB (tab));
	g_return_if_fail ((tab->priv->state == XEDIT_TAB_STATE_NORMAL) ||
			  (tab->priv->state == XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) ||
			  (tab->priv->state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW));
	g_return_if_fail (encoding != NULL);

	g_return_if_fail (tab->priv->tmp_save_uri == NULL);
	g_return_if_fail (tab->priv->tmp_encoding == NULL);

	doc = xedit_tab_get_document (tab);
	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));

	/* reset the save flags, when saving as */
	tab->priv->save_flags = 0;

	if (tab->priv->state == XEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)
	{
		/* We already told the user about the external
		 * modification: hide the message area and set
		 * the save flag.
		 */

		set_message_area (tab, NULL);
		save_flags = tab->priv->save_flags | XEDIT_DOCUMENT_SAVE_IGNORE_MTIME;
	}
	else
	{
		save_flags = tab->priv->save_flags;
	}

	xedit_tab_set_state (tab, XEDIT_TAB_STATE_SAVING);

	/* uri used in error messages... strdup because errors are async
	 * and the string can go away, will be freed in document_saved */
	tab->priv->tmp_save_uri = g_strdup (uri);
	tab->priv->tmp_encoding = encoding;

	if (tab->priv->auto_save_timeout > 0)
		remove_auto_save_timeout (tab);

	/* FIXME: this should behave the same as encoding, setting it here
	   makes it persistent (if save fails, it's remembered). It's not
	   a very big deal, but would be nice to have them follow the
	   same pattern. This can be changed once we break API for 3.0 */
	xedit_document_set_newline_type (doc, newline_type);
	xedit_document_save_as (doc, uri, encoding, tab->priv->save_flags);
}

#define XEDIT_PAGE_SETUP_KEY "xedit-page-setup-key"
#define XEDIT_PRINT_SETTINGS_KEY "xedit-print-settings-key"

static GtkPageSetup *
get_page_setup (XeditTab *tab)
{
	gpointer data;
	XeditDocument *doc;

	doc = xedit_tab_get_document (tab);

	data = g_object_get_data (G_OBJECT (doc),
				  XEDIT_PAGE_SETUP_KEY);

	if (data == NULL)
	{
		return _xedit_app_get_default_page_setup (xedit_app_get_default());
	}
	else
	{
		return gtk_page_setup_copy (GTK_PAGE_SETUP (data));
	}
}

static GtkPrintSettings *
get_print_settings (XeditTab *tab)
{
	gpointer data;
	XeditDocument *doc;
	GtkPrintSettings *settings;
	gchar *uri, *name;

	doc = xedit_tab_get_document (tab);

	data = g_object_get_data (G_OBJECT (doc),
				  XEDIT_PRINT_SETTINGS_KEY);

	if (data == NULL)
	{
		settings = _xedit_app_get_default_print_settings (xedit_app_get_default());
	}
	else
	{
		settings = gtk_print_settings_copy (GTK_PRINT_SETTINGS (data));
	}

	name = xedit_document_get_short_name_for_display (doc);
	uri = g_strconcat ("file://",
			   g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS),
			   "/", name, ".pdf", NULL);

	gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);

	g_free (uri);
	g_free (name);

	return settings;
}

/* FIXME: show the message area only if the operation will be "long" */
static void
printing_cb (XeditPrintJob       *job,
	     XeditPrintJobStatus  status,
	     XeditTab            *tab)
{
	g_return_if_fail (XEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area));	

	gtk_widget_show (tab->priv->message_area);

	xedit_progress_message_area_set_text (XEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area),
					      xedit_print_job_get_status_string (job));

	xedit_progress_message_area_set_fraction (XEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area),
						  xedit_print_job_get_progress (job));
}

static void
store_print_settings (XeditTab      *tab,
		      XeditPrintJob *job)
{
	XeditDocument *doc;
	GtkPrintSettings *settings;
	GtkPageSetup *page_setup;

	doc = xedit_tab_get_document (tab);

	settings = xedit_print_job_get_print_settings (job);

	/* clear n-copies settings since we do not want to
	 * persist that one */
	gtk_print_settings_unset (settings,
				  GTK_PRINT_SETTINGS_N_COPIES);

	/* remember settings for this document */
	g_object_set_data_full (G_OBJECT (doc),
				XEDIT_PRINT_SETTINGS_KEY,
				g_object_ref (settings),
				(GDestroyNotify)g_object_unref);

	/* make them the default */
	_xedit_app_set_default_print_settings (xedit_app_get_default (),
					       settings);

	page_setup = xedit_print_job_get_page_setup (job);

	/* remember page setup for this document */
	g_object_set_data_full (G_OBJECT (doc),
				XEDIT_PAGE_SETUP_KEY,
				g_object_ref (page_setup),
				(GDestroyNotify)g_object_unref);

	/* make it the default */
	_xedit_app_set_default_page_setup (xedit_app_get_default (),
					   page_setup);
}

static void
done_printing_cb (XeditPrintJob       *job,
		  XeditPrintJobResult  result,
		  const GError        *error,
		  XeditTab            *tab)
{
	XeditView *view;

	g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_PRINT_PREVIEWING ||
			  tab->priv->state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW ||
			  tab->priv->state == XEDIT_TAB_STATE_PRINTING);

	if (tab->priv->state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)
	{
		/* print preview has been destroyed... */
		tab->priv->print_preview = NULL;
	}
	else
	{
		g_return_if_fail (XEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area));

		set_message_area (tab, NULL); /* destroy the message area */
	}

	// TODO: check status and error

	if (result ==  XEDIT_PRINT_JOB_RESULT_OK)
	{
		store_print_settings (tab, job);
	}

#if 0
	if (tab->priv->print_preview != NULL)
	{
		/* If we were printing while showing the print preview,
		   see bug #352658 */
		gtk_widget_destroy (tab->priv->print_preview);
		g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_PRINTING);
	}
#endif

	xedit_tab_set_state (tab, XEDIT_TAB_STATE_NORMAL);

	view = xedit_tab_get_view (tab);
	gtk_widget_grab_focus (GTK_WIDGET (view));

 	g_object_unref (tab->priv->print_job);
	tab->priv->print_job = NULL;
}

#if 0
static void
print_preview_destroyed (GtkWidget *preview,
			 XeditTab  *tab)
{
	tab->priv->print_preview = NULL;

	if (tab->priv->state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)
	{
		XeditView *view;

		xedit_tab_set_state (tab, XEDIT_TAB_STATE_NORMAL);

		view = xedit_tab_get_view (tab);
		gtk_widget_grab_focus (GTK_WIDGET (view));
	}
	else
	{
		/* This should happen only when printing while showing the print
		 * preview. In this case let us continue whithout changing
		 * the state and show the document. See bug #352658 */
		gtk_widget_show (tab->priv->view_scrolled_window);
		
		g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_PRINTING);
	}	
}
#endif

static void
show_preview_cb (XeditPrintJob       *job,
		 XeditPrintPreview   *preview,
		 XeditTab            *tab)
{
//	g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_PRINT_PREVIEWING);
	g_return_if_fail (tab->priv->print_preview == NULL);

	set_message_area (tab, NULL); /* destroy the message area */

	tab->priv->print_preview = GTK_WIDGET (preview);
	gtk_box_pack_end (GTK_BOX (tab),
			  tab->priv->print_preview,
			  TRUE,
			  TRUE,
			  0);
	gtk_widget_show (tab->priv->print_preview);
	gtk_widget_grab_focus (tab->priv->print_preview);

/* when the preview gets destroyed we get "done" signal
	g_signal_connect (tab->priv->print_preview,
			  "destroy",
			  G_CALLBACK (print_preview_destroyed),
			  tab);	
*/
	xedit_tab_set_state (tab, XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW);
}

#if 0

static void
set_print_preview (XeditTab  *tab,
		   GtkWidget *print_preview)
{
	if (tab->priv->print_preview == print_preview)
		return;
		
	if (tab->priv->print_preview != NULL)
		gtk_widget_destroy (tab->priv->print_preview);

	tab->priv->print_preview = print_preview;

	gtk_box_pack_end (GTK_BOX (tab),
			  tab->priv->print_preview,
			  TRUE,
			  TRUE,
			  0);		

	gtk_widget_grab_focus (tab->priv->print_preview);

	g_signal_connect (tab->priv->print_preview,
			  "destroy",
			  G_CALLBACK (print_preview_destroyed),
			  tab);
}

static void
preview_finished_cb (GtkSourcePrintJob *pjob, XeditTab *tab)
{
	MatePrintJob *gjob;
	GtkWidget *preview = NULL;

	g_return_if_fail (XEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area));
	set_message_area (tab, NULL); /* destroy the message area */
	
	gjob = gtk_source_print_job_get_print_job (pjob);

	preview = xedit_print_job_preview_new (gjob);	
 	g_object_unref (gjob);
	
	set_print_preview (tab, preview);
	
	gtk_widget_show (preview);
	g_object_unref (pjob);
	
	xedit_tab_set_state (tab, XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW);
}


#endif

static void
print_cancelled (GtkWidget        *area,
                 gint              response_id,
                 XeditTab         *tab)
{
	g_return_if_fail (XEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area));

	xedit_print_job_cancel (tab->priv->print_job);

	g_debug ("print_cancelled");
}

static void
show_printing_message_area (XeditTab *tab, gboolean preview)
{
	GtkWidget *area;

	if (preview)
		area = xedit_progress_message_area_new (GTK_STOCK_PRINT_PREVIEW,
							"",
							TRUE);
	else
		area = xedit_progress_message_area_new (GTK_STOCK_PRINT,
							"",
							TRUE);

	g_signal_connect (area,
			  "response",
			  G_CALLBACK (print_cancelled),
			  tab);
	  
	set_message_area (tab, area);
}

static void
xedit_tab_print_or_print_preview (XeditTab                *tab,
				  GtkPrintOperationAction  print_action)
{
	XeditView *view;
	gboolean is_preview;
	GtkPageSetup *setup;
	GtkPrintSettings *settings;
	GtkPrintOperationResult res;
	GError *error = NULL;

	g_return_if_fail (tab->priv->print_job == NULL);
	g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_NORMAL);

	view = xedit_tab_get_view (tab);

	is_preview = (print_action == GTK_PRINT_OPERATION_ACTION_PREVIEW);

	tab->priv->print_job = xedit_print_job_new (view);
	g_object_add_weak_pointer (G_OBJECT (tab->priv->print_job), 
				   (gpointer *) &tab->priv->print_job);

	show_printing_message_area (tab, is_preview);

	g_signal_connect (tab->priv->print_job,
			  "printing",
			  G_CALLBACK (printing_cb),
			  tab);
	g_signal_connect (tab->priv->print_job,
			  "show-preview",
			  G_CALLBACK (show_preview_cb),
			  tab);
	g_signal_connect (tab->priv->print_job,
			  "done",
			  G_CALLBACK (done_printing_cb),
			  tab);

	if (is_preview)
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_PRINT_PREVIEWING);
	else
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_PRINTING);

	setup = get_page_setup (tab);
	settings = get_print_settings (tab);

	res = xedit_print_job_print (tab->priv->print_job,
				     print_action,
				     setup,
				     settings,
				     GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))),
				     &error);

	// TODO: manage res in the correct way
	if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
	{
		/* FIXME: go in error state */
		xedit_tab_set_state (tab, XEDIT_TAB_STATE_NORMAL);
		g_warning ("Async print preview failed (%s)", error->message);
		g_object_unref (tab->priv->print_job);
		g_error_free (error);
	}
}

void 
_xedit_tab_print (XeditTab     *tab)
{
	g_return_if_fail (XEDIT_IS_TAB (tab));

	/* FIXME: currently we can have just one printoperation going on
	 * at a given time, so before starting the print we close the preview.
	 * Would be nice to handle it properly though */
	if (tab->priv->state == XEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)
	{
		gtk_widget_destroy (tab->priv->print_preview);
	}

	xedit_tab_print_or_print_preview (tab,
					  GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
}

void
_xedit_tab_print_preview (XeditTab     *tab)
{
	g_return_if_fail (XEDIT_IS_TAB (tab));

	xedit_tab_print_or_print_preview (tab,
					  GTK_PRINT_OPERATION_ACTION_PREVIEW);
}

void 
_xedit_tab_mark_for_closing (XeditTab *tab)
{
	g_return_if_fail (XEDIT_IS_TAB (tab));
	g_return_if_fail (tab->priv->state == XEDIT_TAB_STATE_NORMAL);
	
	xedit_tab_set_state (tab, XEDIT_TAB_STATE_CLOSING);
}

gboolean
_xedit_tab_can_close (XeditTab *tab)
{
	XeditDocument *doc;
	XeditTabState  ts;

	g_return_val_if_fail (XEDIT_IS_TAB (tab), FALSE);

	ts = xedit_tab_get_state (tab);

	/* if we are loading or reverting, the tab can be closed */
	if ((ts == XEDIT_TAB_STATE_LOADING)       ||
	    (ts == XEDIT_TAB_STATE_LOADING_ERROR) ||
	    (ts == XEDIT_TAB_STATE_REVERTING)     ||
	    (ts == XEDIT_TAB_STATE_REVERTING_ERROR)) /* CHECK: I'm not sure this is the right behavior for REVERTING ERROR */
		return TRUE;

	/* Do not close tab with saving errors */
	if (ts == XEDIT_TAB_STATE_SAVING_ERROR)
		return FALSE;
		
	doc = xedit_tab_get_document (tab);

	/* TODO: we need to save the file also if it has been externally
	   modified - Paolo (Oct 10, 2005) */

	return (!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)) &&
		!xedit_document_get_deleted (doc));
}

/**
 * xedit_tab_get_auto_save_enabled:
 * @tab: a #XeditTab
 * 
 * Gets the current state for the autosave feature
 * 
 * Return value: %TRUE if the autosave is enabled, else %FALSE
 **/
gboolean
xedit_tab_get_auto_save_enabled	(XeditTab *tab)
{
	xedit_debug (DEBUG_TAB);

	g_return_val_if_fail (XEDIT_IS_TAB (tab), FALSE);

	return tab->priv->auto_save;
}

/**
 * xedit_tab_set_auto_save_enabled:
 * @tab: a #XeditTab
 * @enable: enable (%TRUE) or disable (%FALSE) auto save
 * 
 * Enables or disables the autosave feature. It does not install an
 * autosave timeout if the document is new or is read-only
 **/
void
xedit_tab_set_auto_save_enabled	(XeditTab *tab, 
				 gboolean  enable)
{
	XeditDocument *doc = NULL;
	xedit_debug (DEBUG_TAB);

	g_return_if_fail (XEDIT_IS_TAB (tab));

	doc = xedit_tab_get_document (tab);

	if (tab->priv->auto_save == enable)
		return;

	tab->priv->auto_save = enable;

 	if (enable && 
 	    (tab->priv->auto_save_timeout <=0) &&
 	    !xedit_document_is_untitled (doc) &&
 	    !xedit_document_get_readonly (doc))
 	{
 		if ((tab->priv->state != XEDIT_TAB_STATE_LOADING) &&
		    (tab->priv->state != XEDIT_TAB_STATE_SAVING) &&
		    (tab->priv->state != XEDIT_TAB_STATE_REVERTING) &&
		    (tab->priv->state != XEDIT_TAB_STATE_LOADING_ERROR) &&
		    (tab->priv->state != XEDIT_TAB_STATE_SAVING_ERROR) &&
		    (tab->priv->state != XEDIT_TAB_STATE_REVERTING_ERROR))
		{
			install_auto_save_timeout (tab);
		}
		/* else: the timeout will be installed when loading/saving/reverting
		         will terminate */
		
		return;
	}
 		
 	if (!enable && (tab->priv->auto_save_timeout > 0))
 	{
		remove_auto_save_timeout (tab);
		
 		return; 
 	} 

 	g_return_if_fail ((!enable && (tab->priv->auto_save_timeout <= 0)) ||  
 			  xedit_document_is_untitled (doc) || xedit_document_get_readonly (doc)); 
}

/**
 * xedit_tab_get_auto_save_interval:
 * @tab: a #XeditTab
 * 
 * Gets the current interval for the autosaves
 * 
 * Return value: the value of the autosave
 **/
gint 
xedit_tab_get_auto_save_interval (XeditTab *tab)
{
	xedit_debug (DEBUG_TAB);

	g_return_val_if_fail (XEDIT_IS_TAB (tab), 0);

	return tab->priv->auto_save_interval;
}

/**
 * xedit_tab_set_auto_save_interval:
 * @tab: a #XeditTab
 * @interval: the new interval
 * 
 * Sets the interval for the autosave feature. It does nothing if the
 * interval is the same as the one already present. It removes the old
 * interval timeout and adds a new one with the autosave passed as
 * argument.
 **/
void 
xedit_tab_set_auto_save_interval (XeditTab *tab, 
				  gint      interval)
{
	XeditDocument *doc = NULL;
	
	xedit_debug (DEBUG_TAB);
	
	g_return_if_fail (XEDIT_IS_TAB (tab));

	doc = xedit_tab_get_document(tab);

	g_return_if_fail (XEDIT_IS_DOCUMENT (doc));
	g_return_if_fail (interval > 0);

	if (tab->priv->auto_save_interval == interval)
		return;

	tab->priv->auto_save_interval = interval;
		
	if (!tab->priv->auto_save)
		return;

	if (tab->priv->auto_save_timeout > 0)
	{
		g_return_if_fail (!xedit_document_is_untitled (doc));
		g_return_if_fail (!xedit_document_get_readonly (doc));

		remove_auto_save_timeout (tab);

		install_auto_save_timeout (tab);
	}
}

void
xedit_tab_set_info_bar (XeditTab  *tab,
                        GtkWidget *info_bar)
{
	g_return_if_fail (XEDIT_IS_TAB (tab));
	g_return_if_fail (info_bar == NULL || GTK_IS_WIDGET (info_bar));

	/* FIXME: this can cause problems with the tab state machine */
	set_message_area (tab, info_bar);
}
