Index: components/editor/gtkhtml-editor-common.h
===================================================================
--- components/editor/gtkhtml-editor-common.h	(revision 8831)
+++ components/editor/gtkhtml-editor-common.h	(working copy)
@@ -26,6 +26,7 @@
 #endif /* HAVE_CONFIG_H */
 
 #include <glib.h>
+#include <gio/gio.h>
 #include <gtk/gtk.h>
 
 #endif /* GTKHTML_EDITOR_COMMON_H */
Index: components/editor/gtkhtml-editor-actions.c
===================================================================
--- components/editor/gtkhtml-editor-actions.c	(revision 8831)
+++ components/editor/gtkhtml-editor-actions.c	(working copy)
@@ -20,6 +20,7 @@
 
 #include "gtkhtml-editor-private.h"
 
+#include <string.h>
 #include <libgnome/gnome-url.h>
 
 /******************************************************************************
@@ -136,82 +137,6 @@
  *****************************************************************************/
 
 static void
-insert_image_response_cb (GtkFileChooser *file_chooser,
-                          gint response,
-                          GtkhtmlEditor *editor)
-{
-	HTMLObject *image;
-	GtkHTML *html;
-	gchar *uri;
-
-	if (response != GTK_RESPONSE_OK)
-		return;
-
-	html = gtkhtml_editor_get_html (editor);
-	uri = gtk_file_chooser_get_uri (file_chooser);
-	image = html_image_new (
-		html_engine_get_image_factory (html->engine), uri,
-		NULL, NULL, 0, 0, 0, 0, 0, NULL, HTML_VALIGN_NONE, FALSE);
-	html_engine_paste_object (html->engine, image, 1);
-	g_free (uri);
-}
-
-static void
-insert_html_file_response_cb (GtkFileChooser *file_chooser,
-                              gint response,
-                              GtkhtmlEditor *editor)
-{
-	gchar *filename;
-	gchar *contents;
-	gsize length;
-	GError *error = NULL;
-
-	if (response != GTK_RESPONSE_OK)
-		return;
-
-	filename = gtk_file_chooser_get_filename (file_chooser);
-
-	/* Assume the file encoding is UTF-8. */
-	gtkhtml_editor_get_file_contents (
-		filename, NULL, &contents, &length, &error);
-
-	/* If we get a conversion error, look for a charset and try again. */
-	if (error != NULL && g_error_matches (error, G_CONVERT_ERROR,
-		G_CONVERT_ERROR_ILLEGAL_SEQUENCE)) {
-
-		gchar *charset;
-
-		charset = gtkhtml_editor_get_file_charset (filename);
-		if (charset != NULL) {
-			g_clear_error (&error);
-			gtkhtml_editor_get_file_contents (
-				filename, charset, &contents, &length, &error);
-			g_free (charset);
-		}
-	}
-
-	if (error == NULL) {
-		GtkHTML *html;
-		GtkHTMLStream *stream;
-
-		html = GTK_HTML (gtk_html_new ());
-		stream = gtk_html_begin_content (
-			html, "text/html; charset=utf-8");
-		gtk_html_write (html, stream, contents, length);
-		gtk_html_end (html, stream, GTK_HTML_STREAM_OK);
-		gtk_html_insert_gtk_html (
-			gtkhtml_editor_get_html (editor), html);
-		g_free (contents);
-	} else {
-		/* XXX Show an error dialog. */
-		g_warning ("%s", error->message);
-		g_error_free (error);
-	}
-
-	g_free (filename);
-}
-
-static void
 insert_emoticon (GtkhtmlEditor *editor,
                  GtkAction *action,
                  const gchar *alt)
@@ -249,55 +174,6 @@ insert_emoticon (GtkhtmlEditor *editor,
 }
 
 static void
-insert_text_file_response_cb (GtkFileChooser *file_chooser,
-                              gint response,
-                              GtkhtmlEditor *editor)
-{
-	gchar *contents;
-	gchar *filename;
-	gsize length;
-	GError *error = NULL;
-
-	if (response != GTK_RESPONSE_OK)
-		return;
-
-	filename = gtk_file_chooser_get_filename (file_chooser);
-
-	/* Assume the file encoding is UTF-8. */
-	gtkhtml_editor_get_file_contents (
-		filename, NULL, &contents, &length, &error);
-
-	/* If we get a conversion error, look for a charset and try again. */
-	if (error != NULL && g_error_matches (error, G_CONVERT_ERROR,
-		G_CONVERT_ERROR_ILLEGAL_SEQUENCE)) {
-
-		gchar *charset;
-
-		charset = gtkhtml_editor_get_file_charset (filename);
-		if (charset != NULL) {
-			g_clear_error (&error);
-			gtkhtml_editor_get_file_contents (
-				filename, charset, &contents, &length, &error);
-			g_free (charset);
-		}
-	}
-
-	if (error == NULL) {
-		GtkHTML *html;
-
-		html = gtkhtml_editor_get_html (editor);
-		html_engine_paste_text (html->engine, contents, length);
-		g_free (contents);
-	} else {
-		/* XXX Show an error dialog. */
-		g_warning ("%s", error->message);
-		g_error_free (error);
-	}
-
-	g_free (filename);
-}
-
-static void
 replace_ask_cb (HTMLEngine *engine,
                 gpointer data)
 {
@@ -691,18 +567,81 @@ static void
 action_insert_html_file_cb (GtkToggleAction *action,
                             GtkhtmlEditor *editor)
 {
-	gtkhtml_editor_insert_file (
-		editor, _("Insert HTML File"),
-		G_CALLBACK (insert_html_file_response_cb));
+	GFile *file;
+	GtkHTML *html;
+	GtkHTMLStream *stream;
+	gchar *contents = NULL;
+	gchar *utf8;
+	gsize length;
+	GError *error = NULL;
+
+	/* Ask the user to choose a file. */
+	file = gtkhtml_editor_choose_file (editor, _("Insert HTML File"));
+	if (file == NULL)
+		return;
+
+	/* Load the file contents into memory. */
+	gtkhtml_editor_get_file_contents (
+		editor, file, &contents, &length, &error);
+
+	if (error != NULL)
+		goto fail;
+
+	/* Convert the contents to UTF-8. */
+	utf8 = gtkhtml_editor_convert_to_utf8 (contents, length, &error);
+	g_free (contents);
+
+	if (error != NULL)
+		goto fail;
+
+	/* Insert the contents into the composer. */
+	html = GTK_HTML (gtk_html_new ());
+	stream = gtk_html_begin_content (html, "text/html; charset=utf-8");
+	gtk_html_write (html, stream, utf8, strlen (utf8));
+	gtk_html_end (html, stream, GTK_HTML_STREAM_OK);
+
+	gtk_html_insert_gtk_html (gtkhtml_editor_get_html (editor), html);
+
+	g_free (utf8);
+
+fail:
+	if (error != NULL) {
+		GtkWidget *dialog;
+
+		dialog = gtk_message_dialog_new (
+			GTK_WINDOW (editor), 0, GTK_MESSAGE_ERROR,
+			GTK_BUTTONS_CLOSE, _("Failed to insert HTML file."));
+		gtk_message_dialog_format_secondary_text (
+			GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+		g_error_free (error);
+	}
+
+	g_object_unref (file);
 }
 
 static void
 action_insert_image_cb (GtkAction *action,
                         GtkhtmlEditor *editor)
 {
-	gtkhtml_editor_insert_file (
-		editor, _("Insert Image"),
-		G_CALLBACK (insert_image_response_cb));
+	HTMLObject *image;
+	GtkHTML *html;
+	GFile *file;
+	gchar *uri;
+
+	html = gtkhtml_editor_get_html (editor);
+
+	file = gtkhtml_editor_choose_file (editor, _("Insert Image"));
+	if (file == NULL)
+		return;
+
+	uri = g_file_get_uri (file);
+	image = html_image_new (
+		html_engine_get_image_factory (html->engine), uri,
+		NULL, NULL, 0, 0, 0, 0, 0, NULL, HTML_VALIGN_NONE, FALSE);
+	html_engine_paste_object (html->engine, image, 1);
+	g_free (uri);
 }
 
 static void
@@ -866,9 +805,52 @@ static void
 action_insert_text_file_cb (GtkAction *action,
                             GtkhtmlEditor *editor)
 {
-	gtkhtml_editor_insert_file (
-		editor, _("Insert Text File"),
-		G_CALLBACK (insert_text_file_response_cb));
+	GFile *file;
+	GtkHTML *html;
+	gchar *contents = NULL;
+	gchar *utf8;
+	gsize length;
+	GError *error = NULL;
+
+	/* Ask the user to choose a file. */
+	file = gtkhtml_editor_choose_file (editor, _("Insert Text File"));
+	if (file == NULL)
+		return;
+
+	/* Load the file contents into memory. */
+	gtkhtml_editor_get_file_contents (
+		editor, file, &contents, &length, &error);
+
+	if (error != NULL)
+		goto fail;
+
+	/* Convert the contents to UTF-8. */
+	utf8 = gtkhtml_editor_convert_to_utf8 (contents, length, &error);
+	g_free (contents);
+
+	if (error != NULL)
+		goto fail;
+
+	/* Insert the contents into the composer. */
+	html = gtkhtml_editor_get_html (editor);
+	html_engine_paste_text (html->engine, utf8, -1);
+	g_free (utf8);
+
+fail:
+	if (error != NULL) {
+		GtkWidget *dialog;
+
+		dialog = gtk_message_dialog_new (
+			GTK_WINDOW (editor), 0, GTK_MESSAGE_ERROR,
+			GTK_BUTTONS_CLOSE, _("Failed to insert text file."));
+		gtk_message_dialog_format_secondary_text (
+			GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+		g_error_free (error);
+	}
+
+	g_object_unref (file);
 }
 
 static void
Index: components/editor/gtkhtml-editor-private.c
===================================================================
--- components/editor/gtkhtml-editor-private.c	(revision 8832)
+++ components/editor/gtkhtml-editor-private.c	(working copy)
@@ -440,89 +440,144 @@ gtkhtml_editor_find_data_file (const gch
 }
 
 gchar *
-gtkhtml_editor_get_file_charset (const gchar *filename)
+gtkhtml_editor_convert_to_utf8 (const gchar *text,
+                                gssize length,
+                                GError **error)
 {
 	GRegex *regex;
 	GMatchInfo *match_info;
-	gchar *charset = NULL;
-	gchar *contents;
-	GError *error = NULL;
+	gchar *utf8;
 
-	g_return_val_if_fail (filename != NULL, NULL);
+	g_return_val_if_fail (text != NULL, NULL);
 
-	if (!g_file_get_contents (filename, &contents, NULL, &error))
-		goto exit;
+	/* First check if this is already valid UTF-8. */
+	if (g_utf8_validate (text, length, NULL))
+		return g_strdup (text);
 
 	/* Search for "charset = token" as defined in RFC 2616. */
 	regex = g_regex_new (
 		"charset[ \t]*=[ \t]*(" TOKEN ")",
-		G_REGEX_CASELESS, 0, &error);
-	if (regex == NULL)
-		goto exit;
-
-	/* Extract "token", which should be the charset name. */
-	g_regex_match (regex, contents, 0, &match_info);
-	if (g_match_info_matches (match_info))
+		G_REGEX_CASELESS, 0, error);
+	g_return_val_if_fail (regex != NULL, NULL);
+
+	/* Did we get a match? */
+	if (g_regex_match (regex, text, 0, &match_info)) {
+		gchar *charset;
+
+		/* Extract "token", which should be the charset name,
+		 * and attempt to convert the text to UTF-8. */
 		charset = g_match_info_fetch (match_info, 1);
+		utf8 = g_convert (
+			text, length, "UTF-8",
+			charset, NULL, NULL, error);
+		g_free (charset);
+	} else {
+		/* Otherwise try converting the text to UTF-8 using
+		 * the character set for the current locale. */
+		utf8 = g_locale_to_utf8 (text, length, NULL, NULL, error);
+	}
 
 	g_match_info_free (match_info);
 	g_regex_unref (regex);
 
-exit:
-	if (error != NULL) {
-		g_warning ("%s", error->message);
-		g_error_free (error);
-	}
+	return utf8;
+}
 
-	g_free (contents);
+/* Helper for gtkhtml_editor_get_file_contents(). */
+static void
+editor_load_contents_finished (GFile *file,
+                               GAsyncResult *result,
+                               GtkDialog *dialog)
+{
+	/* Embed the result into the GtkDialog object. */
+	g_object_set_data_full (
+		G_OBJECT (dialog), "result", g_object_ref (result),
+		(GDestroyNotify) g_object_unref);
 
-	return charset;
+	/* Notify the dialog that we're finished. */
+	gtk_dialog_response (dialog, GTK_RESPONSE_OK);
 }
 
 gboolean
-gtkhtml_editor_get_file_contents (const gchar *filename,
-                                  const gchar *encoding,
+gtkhtml_editor_get_file_contents (GtkhtmlEditor *editor,
+                                  GFile *file,
                                   gchar **contents,
                                   gsize *length,
                                   GError **error)
 {
-	GIOChannel *channel;
-	GIOStatus status;
+	GCancellable *cancellable;
+	GFileInfo *file_info;
+	GAsyncResult *result;
+	GtkWidget *dialog;
+	gboolean success = FALSE;
 
-	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (GTKHTML_IS_EDITOR (editor), FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
 	g_return_val_if_fail (contents != NULL, FALSE);
 
-	channel = g_io_channel_new_file (filename, "r", error);
-	if (channel == NULL)
+	/* Native files do not need cancellation. */
+	if (g_file_is_native (file))
+		return g_file_load_contents (
+			file, NULL, contents, length, NULL, error);
+
+	file_info = g_file_query_info (
+		file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+		G_FILE_QUERY_INFO_NONE, NULL, error);
+	if (file_info == NULL)
 		return FALSE;
 
-	status = g_io_channel_set_encoding (channel, encoding, error);
-	if (status == G_IO_STATUS_ERROR) {
-		g_io_channel_unref (channel);
-		return FALSE;
-	}
-
-	status = g_io_channel_read_to_end (channel, contents, length, error);
-	if (status == G_IO_STATUS_ERROR) {
-		g_io_channel_unref (channel);
-		return FALSE;
-	}
+	/* Build a cancellation dialog. */
+	dialog = gtk_message_dialog_new (
+		GTK_WINDOW (editor), 0, GTK_MESSAGE_INFO,
+		GTK_BUTTONS_CANCEL, _("Loading \"%s\""),
+		g_file_info_get_display_name (file_info));
+	gtk_message_dialog_format_secondary_text (
+		GTK_MESSAGE_DIALOG (dialog),
+		_("You can stop this operation by clicking cancel."));
+
+	cancellable = g_cancellable_new ();
+
+	/* Start an asynchronous operation. */
+	g_file_load_contents_async (
+		file, cancellable, (GAsyncReadyCallback) 
+		editor_load_contents_finished, dialog);
+
+	/* Run the cancellation dialog. */
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_CANCEL)
+		g_cancellable_cancel (cancellable);
+
+	/* XXX This is really dirty, but I don't know of a better way.
+	 *     Basically I want to "join" with the asynchronous operation
+	 *     without using thread synchronization primitives.  At this
+	 *     point the operation should either already be finished or
+	 *     in the process of being cancelled.  The GAsyncReadyCallback
+	 *     receives the GAsyncResult I need, so I'm going to just spin
+	 *     the main loop until it gets called. */
+	result = g_object_get_data (G_OBJECT (dialog), "result");
+	while (result == NULL && !gtk_main_iteration ())
+		result = g_object_get_data (G_OBJECT (dialog), "result");
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+	/* Get the results of the asynchronous operation. */
+	success = g_file_load_contents_finish (
+		file, result, contents, length, NULL, error);
 
-	g_io_channel_unref (channel);
+	gtk_widget_destroy (dialog);
+	g_object_unref (cancellable);
+	g_object_unref (file_info);
 
-	return TRUE;
+	return success;
 }
 
-gint
-gtkhtml_editor_insert_file (GtkhtmlEditor *editor,
-                            const gchar *title,
-                            GCallback response_cb)
+GFile *
+gtkhtml_editor_choose_file (GtkhtmlEditor *editor,
+                            const gchar *title)
 {
 	GtkWidget *dialog;
+	GFile *file;
 	gint response;
 
-	g_return_val_if_fail (GTKHTML_IS_EDITOR (editor), GTK_RESPONSE_CANCEL);
-	g_return_val_if_fail (response_cb != NULL, GTK_RESPONSE_CANCEL);
+	g_return_val_if_fail (GTKHTML_IS_EDITOR (editor), NULL);
 
 	dialog = gtk_file_chooser_dialog_new (
 		title, GTK_WINDOW (editor),
@@ -534,13 +589,21 @@ gtkhtml_editor_insert_file (GtkhtmlEdito
 	gtk_dialog_set_default_response (
 		GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 
-	g_signal_connect (dialog, "response", response_cb, editor);
-
 	response = gtkhtml_editor_file_chooser_dialog_run (editor, dialog);
 
+	if (response == GTK_RESPONSE_OK) {
+		gchar *uri;
+
+		/* XXX Wish: gtk_file_chooser_get_file() */
+		uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
+		file = g_file_new_for_uri (uri);
+		g_free (uri);
+	} else
+		file = NULL;
+
 	gtk_widget_destroy (dialog);
 
-	return response;
+	return file;
 }
 
 void
Index: components/editor/gtkhtml-editor-private.h
===================================================================
--- components/editor/gtkhtml-editor-private.h	(revision 8831)
+++ components/editor/gtkhtml-editor-private.h	(working copy)
@@ -174,15 +174,16 @@ void		gtkhtml_editor_private_finalize	 (
 
 void		gtkhtml_editor_actions_init	 (GtkhtmlEditor *editor);
 gchar *		gtkhtml_editor_find_data_file	 (const gchar *basename);
-gchar *		gtkhtml_editor_get_file_charset	 (const gchar *filename);
-gboolean	gtkhtml_editor_get_file_contents (const gchar *filename,
-						  const gchar *encoding,
+gchar *		gtkhtml_editor_convert_to_utf8	 (const gchar *text,
+						  gssize length,
+						  GError **error);
+gboolean	gtkhtml_editor_get_file_contents (GtkhtmlEditor *editor,
+						  GFile *file,
 						  gchar **contents,
 						  gsize *length,
 						  GError **error);
-gint		gtkhtml_editor_insert_file	 (GtkhtmlEditor *editor,
-						  const gchar *title,
-						  GCallback response_cb);
+GFile *		gtkhtml_editor_choose_file	 (GtkhtmlEditor *editor,
+						  const gchar *title);
 void		gtkhtml_editor_spell_check	 (GtkhtmlEditor *editor,
 						  gboolean whole_document);
 gboolean	gtkhtml_editor_next_spell_error	 (GtkhtmlEditor *editor);
Index: configure.in
===================================================================
--- configure.in	(revision 8831)
+++ configure.in	(working copy)
@@ -7,6 +7,7 @@ PACKAGE=gtkhtml
 
 # Required Package Versions
 m4_define([gtk_minimum_version], [2.12.0])
+m4_define([glib_minimum_version], [2.16.0])
 m4_define([gail_minimum_version], [1.1.0])
 m4_define([gnome_icon_theme_minimum_version], [1.2.0])
 m4_define([libbonobo_minimum_version], [2.20.3])
@@ -84,7 +85,7 @@ AC_SUBST(GAIL_CFLAGS)
 AC_SUBST(GAIL_LIBS)
 AC_SUBST(GAIL_MODULES)
 
-GTKHTML_MODULES="gtk+-2.0 >= gtk_minimum_version libgnomeui-2.0 >= libgnomeui_minimum_version libglade-2.0 >= libglade_minimum_version enchant gconf-2.0 iso-codes"
+GTKHTML_MODULES="gtk+-2.0 >= gtk_minimum_version gio-2.0 >= glib_minimum_version libgnomeui-2.0 >= libgnomeui_minimum_version libglade-2.0 >= libglade_minimum_version enchant gconf-2.0 iso-codes"
 PKG_CHECK_MODULES(GTKHTML, $GTKHTML_MODULES)
 AC_SUBST(GTKHTML_CFLAGS)
 AC_SUBST(GTKHTML_LIBS)
