[Xfce4-commits] <mousepad:master> * Diffstat: 38 files changed, 5987 insertions(+), 3632 deletions(-)

Nick Schermer noreply at xfce.org
Sat May 5 21:30:41 CEST 2012


Updating branch refs/heads/master
         to d1196e9f4486f9a5d4610fb88a1076a5e8d476a5 (commit)
       from 772eeca118e48c94c5fe908c333197b3b6b8845e (commit)

commit d1196e9f4486f9a5d4610fb88a1076a5e8d476a5
Author: Nick Schermer <nick at xfce.org>
Date:   Tue Oct 16 20:01:37 2007 +0000

    * Diffstat:
      38 files changed, 5987 insertions(+), 3632 deletions(-)
    
    * mousepad/mousepad-dialogs.c: Set the correct default return
      in the jump dialog.
    * mousepad/mousepad-replace-{dialog,window,preferences}.{c,h}:
      Implement the new replace dialog.
    * Remove options from the type-ahead bar, it was too bloated
      and those options are now available in the replace dialog.
    * mousepad/mousepad-document.c: The MousepadDocument now holds
      utf-8 valid names, the MousepadFile the real filename.
    * mousepad/mousepad-window.c: Full tab dnd (detach) with
      Gtk+ 2.12.
    * mousepad/mousepad-encoding-dialog.{c,h}: Add a encoding dialog.
      This dialog should help users to find the right encoding for a
      document.
    * mousepad/mousepad-print.{c,h}: Initial version of a basic
      printing support. Needs some big improvement tho.
    * mousepad/mousepad-statusbar: Fix issues with some theme engines
      that paint a line above the statusbar (instead of a frame).
    * mousepad/mousepad-window.c: A window is now destoyed when it
      contains no tabs, previously this caused some segfaults with the
      tab dnd code, but this is all fixed now.
    * mousepad/mousepad-preferences.c: The option names are now stored
      in the nick name of the pspec. With debug build this name is
      compared with the option name from spec name to check for typos.
    * mousepad/mousepad-view.c: Options for 'insert tab as spaces' and
      settings the tab size. This also needed a rewrite of the
      indentation code, which ended up much cleaner.
    * mousepad/mousepad-view.c: Big speed improvements in the vertical
      selction code.
    * mousepad/mousepad-view.c: Cleanups in the clipboard code and move
      code from mousepad-document to mousepad-view.
    * mousepad/mousepad-{window,search-bar}.c: Handle clipboard actions
      properly when the search bar is focused.
    * A lot of code cleanups, bug fixes, polishing and stuff I can't
      remember after 5 months ;).
    
    
    (Old svn revision: 26134)

 ChangeLog                           |   37 +
 NEWS                                |   53 +-
 TODO                                |   44 +-
 configure.in.in                     |   27 +-
 mousepad/Makefile.am                |   44 +-
 mousepad/main.c                     |   13 +-
 mousepad/mousepad-application.c     |   26 +-
 mousepad/mousepad-dialogs.c         |  123 +---
 mousepad/mousepad-dialogs.h         |   33 +-
 mousepad/mousepad-document.c        | 1008 +++++----------------------
 mousepad/mousepad-document.h        |  122 ++--
 mousepad/mousepad-encoding-dialog.c |  577 +++++++++++++++
 mousepad/mousepad-encoding-dialog.h |   42 ++
 mousepad/mousepad-file.c            |  798 ++++++++++++++--------
 mousepad/mousepad-file.h            |   72 ++-
 mousepad/mousepad-marshal.list      |    3 +-
 mousepad/mousepad-preferences.c     |  412 ++++++------
 mousepad/mousepad-print.c           |  277 ++++++++
 mousepad/mousepad-print.h           |   44 ++
 mousepad/mousepad-private.h         |   45 +-
 mousepad/mousepad-replace-dialog.c  |  665 ++++++++++++++++++
 mousepad/mousepad-replace-dialog.h  |   41 ++
 mousepad/mousepad-search-bar.c      |  459 +++++--------
 mousepad/mousepad-search-bar.h      |   12 +-
 mousepad/mousepad-statusbar.c       |   70 +--
 mousepad/mousepad-statusbar.h       |    3 -
 mousepad/mousepad-types.h           |   34 -
 mousepad/mousepad-undo.c            |  731 +++++++++++--------
 mousepad/mousepad-undo.h            |   10 +-
 mousepad/mousepad-util.c            |  615 ++++++++++++++++
 mousepad/mousepad-util.h            |   92 +++
 mousepad/mousepad-view.c            | 1266 +++++++++++++++++++++-------------
 mousepad/mousepad-view.h            |   40 +-
 mousepad/mousepad-window-ui.xml     |    1 +
 mousepad/mousepad-window.c          | 1321 ++++++++++++++++++++---------------
 mousepad/mousepad-window.h          |    6 +-
 po/POTFILES.in                      |    9 +-
 po/mousepad.pot                     |  483 +++++++------
 38 files changed, 6007 insertions(+), 3651 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0f46b2b..8c92023 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,40 @@
+2007-10-16      Nick Schermer <nick at xfce.org>
+	* mousepad/mousepad-dialogs.c: Set the correct default return
+	  in the jump dialog.
+	* mousepad/mousepad-replace-{dialog,window,preferences}.{c,h}:
+	  Implement the new replace dialog.
+	* Remove options from the type-ahead bar, it was too bloated
+	  and those options are now available in the replace dialog.
+	* mousepad/mousepad-document.c: The MousepadDocument now holds
+	  utf-8 valid names, the MousepadFile the real filename.
+	* mousepad/mousepad-window.c: Full tab dnd (detach) with
+	  Gtk+ 2.12.
+	* mousepad/mousepad-encoding-dialog.{c,h}: Add a encoding dialog.
+	  This dialog should help users to find the right encoding for a
+	  document.
+	* mousepad/mousepad-print.{c,h}: Initial version of a basic 
+	  printing support. Needs some big improvement tho.
+	* mousepad/mousepad-statusbar: Fix issues with some theme engines
+	  that paint a line above the statusbar (instead of a frame).
+	* mousepad/mousepad-window.c: A window is now destoyed when it 
+	  contains no tabs, previously this caused some segfaults with the
+	  tab dnd code, but this is all fixed now.
+	* mousepad/mousepad-preferences.c: The option names are now stored
+	  in the nick name of the pspec. With debug build this name is
+	  compared with the option name from spec name to check for typos.
+	* mousepad/mousepad-view.c: Options for 'insert tab as spaces' and
+	  settings the tab size. This also needed a rewrite of the
+	  indentation code, which ended up much cleaner.
+	* mousepad/mousepad-view.c: Big speed improvements in the vertical
+	  selction code.
+	* mousepad/mousepad-view.c: Cleanups in the clipboard code and move
+	  code from mousepad-document to mousepad-view.
+	* mousepad/mousepad-{window,search-bar}.c: Handle clipboard actions
+	  properly when the search bar is focused.
+	* A lot of code cleanups, bug fixes, polishing and stuff I can't
+	  remember after 5 months ;).
+
+
 2007-05-22	Nick Schermer <nick at xfce.org
 	* *.*: Remove copyright lines.
 
diff --git a/NEWS b/NEWS
index b7922bb..dcfac04 100644
--- a/NEWS
+++ b/NEWS
@@ -1,27 +1,30 @@
 0.3.0
 =====
-- A complete rewrite of Mousepad. The code is now written in GOjects 
-  where possible so we have a solid base for further development.
-- Run multiple windows in one instance. We also invoke a running
-  instance with DBus (optional), this is needed for tab DND between
-  windows.
-- Support for multiple tabs in one window.
-- Load multiple files at once from both the open dialog and the command
-  line.
-- Type-ahead find and highlight feature like Firefox.
-- Support for editable menu accelerators.
-- Reimplemented recent file support. You can cleanup the Mousepad history
-  and a file is removed from the history when Mousepad was unable to open
-  it.
-- Various hidden settings.
-- Statusbar with cursor location and whether overwrite is actived.
-- Mousepad depends on GTK+ 2.10.
-- Save button is now default response in the dialog (Bug #2941).
-- No font is set as long the user has not defined any (Bug #2720).
-- No compiler warnings (Bug #1697).
-- Windows geometry is now properly saved (Bug #2945).
-- Hidden option to show to full path in the window title (Bug #2896).
-- Basic command line options (Bug #2397).
-- The MousepadPreferences object uses XfceRc for saving the 
-  settings (Bug #2786).
-- No crashes when there is no rc file (Bug #2784).
+Highlights:
+    - A complete rewrite of Mousepad. The code is now written in GOjects
+      where possible so we have a solid base for further development.
+    - Run multiple windows in one instance. We also invoke a running
+      instance with DBus (optional), this is needed for tab DND between
+      windows.
+    - Support for multiple tabs in one window.
+    - Load multiple files at once from both the open dialog and the command
+      line.
+    - Type-ahead find and highlight feature like Firefox.
+    - Support for editable menu accelerators.
+    - Reimplemented recent file support. You can cleanup the Mousepad history
+      and a file is removed from the history when Mousepad was unable to open
+      it.
+    - Various hidden settings.
+    - Statusbar with cursor location and whether overwrite is actived.
+    - Mousepad depends on GTK+ 2.10.
+
+Bug fixes:
+    - Save button is now default response in the dialog (Bug #2941).
+    - No font is set as long the user has not defined any (Bug #2720).
+    - No compiler warnings (Bug #1697).
+    - Windows geometry is now properly saved (Bug #2945).
+    - Hidden option to show the full path in the window title (Bug #2896).
+    - Basic command line options (Bug #2397).
+    - The MousepadPreferences object uses XfceRc for saving the
+      settings (Bug #2786).
+    - No segfault when there is no rc file (Bug #2784).
diff --git a/TODO b/TODO
index b475bf5..db96516 100644
--- a/TODO
+++ b/TODO
@@ -8,60 +8,44 @@ from the Xfce schedule).
 
 Interface
 =========
-- Dialog when closing multiple (modified) tabs. Maybe not needed
-  since we ask the use what to do when a modified file is closed.
 - Tab font color when the file is modified or read only.
 - Test the application with a screen reader and add Atk objects
   where needed.
 - Cleanup the menus and make them consistent. For example: we're
   using both file and document atm. The name of the Go menu should
   also be changed to something more descriptive.
-- You can't use the Ctrl - v/c/x buttons in the type-ahead bar
-  because they are 'registered' by the textview/ui.
-- When hitting the enter button in the jump dialog, we should jump.
-- Replace dialog. This dialog should also provide a find button so
-  there is an alternative for the type-ahead feature.
-- Maybe a match whole word option.
+- Make the mnemonic's consistent all over the application and make
+  sure they don't conflict inside a dialog/window.
+- Check the search and replace code for bugs.
+- Add option to disable the search feedback in the replace dialog
+  (highlight, count matches, type-ahead). This can slow down mousepad
+  with large text files.
+
+
+Undo
+====
+- Need to fix a couple of issues here. I'm not really satisfied with
+  it right now.
 
 
 Code
 ====
-- Cleanup mousepad-document.h. The order of the functions is a mess.
 - Check for code duplication and make sure the comments are worth
   reading.
 
 
-Text View
-=========
-- Tabs as spaces option.
-
-
-Undo Manager
-============
-- Replace g_list_append with g_list_prepend in the undo manager. See
-  glib manual why.
-- We erase the GString in the undo manager, but this buffer will be
-  (very) large when a large bunch of text is deleted. Maybe not a big
-  problem, but we could shrink it after erasing.
-- Let the undo manager handle multiple steps (mostly for vertical
-  selection cut/delete).
-
-
 Saving and loading
 ==================
 - Save All option.
-- Support for encoding. Only UTF-8 is supported for testing.
-- The filenames used in the title and tooltips are not UTF-8 safe.
-  We also have to make sure _ characters in a filename do not
-  result in mnemonic labels.
 - Check if the file is externally modified when switching tabs.
+- Cleanup and improve encoding support and order the encoding types.
 
 
 Other
 =====
-- Implement Gtk print support.
 - We need a cool Mousepad logo.
 - Write documentation.
+- Session manager support.
 
 
 Testing and polishing
diff --git a/configure.in.in b/configure.in.in
index 671b455..dc79f57 100644
--- a/configure.in.in
+++ b/configure.in.in
@@ -69,31 +69,8 @@ AC_SUBST([MOUSEPAD_VERSION_MICRO])
 dnl **********************************
 dnl *** Check for standard headers ***
 dnl **********************************
-AC_CHECK_HEADERS([ctype.h errno.h fcntl.h memory.h stdlib.h string.h \
-                  sys/mman.h sys/types.h sys/stat.h time.h unistd.h])
-
-dnl ************************************
-dnl *** Check for standard functions ***
-dnl ************************************
-AC_FUNC_MMAP()
-AC_CHECK_FUNCS([posix_madvise])
-
-dnl ******************************************
-dnl *** Linux/glibc specified work-arounds ***
-dnl ******************************************
-AC_MSG_CHECKING([whether we need _BSD_SOURCE and _XOPEN_SOURCE])
-AC_TRY_LINK([#include <features.h>],
-[
-  if (__GLIBC_PREREQ (2, 0));
-],
-[
-  AC_DEFINE([_XOPEN_SOURCE], [600], [Required to unbreak glibc])
-  AC_DEFINE([_BSD_SOURCE], [1], [Required to unbreak glibc])
-  AC_MSG_RESULT([yes])
-],
-[
-  AC_MSG_RESULT([no])
-])
+AC_CHECK_HEADERS([errno.h fcntl.h memory.h math.h stdlib.h string.h \
+                  sys/types.h sys/stat.h time.h unistd.h])
 
 dnl ******************************
 dnl *** Check for i18n support ***
diff --git a/mousepad/Makefile.am b/mousepad/Makefile.am
index 68f49f9..141d88b 100644
--- a/mousepad/Makefile.am
+++ b/mousepad/Makefile.am
@@ -10,22 +10,17 @@ INCLUDES =									\
 	-DG_LOG_DOMAIN=\"Mousepad\"						\
 	-DLIBEXECDIR=\"$(libexecdir)\"						\
 	-DPACKAGE_LOCALE_DIR=\"$(localedir)\"					\
+	-DG_DISABLE_DEPRECATED							\
 	$(PLATFORM_CPPFLAGS)
 
 bin_PROGRAMS = 									\
 	mousepad
 
-mousepad_headers = 								\
-	mousepad-types.h
-
 mousepad_built_sources =							\
-	mousepad-enum-types.c							\
-	mousepad-enum-types.h							\
 	mousepad-marshal.c							\
 	mousepad-marshal.h
 
 mousepad_SOURCES =								\
-	$(mousepad_headers)							\
 	$(mousepad_built_sources)						\
 	$(mousepad_dbus_sources)						\
 	main.c									\
@@ -35,11 +30,17 @@ mousepad_SOURCES =								\
 	mousepad-dialogs.h							\
 	mousepad-document.c							\
 	mousepad-document.h							\
+	mousepad-encoding-dialog.c						\
+	mousepad-encoding-dialog.h						\
 	mousepad-file.c								\
 	mousepad-file.h								\
 	mousepad-preferences.c							\
 	mousepad-preferences.h							\
+	mousepad-print.c							\
+	mousepad-print.h							\
 	mousepad-private.h							\
+	mousepad-replace-dialog.c						\
+	mousepad-replace-dialog.h						\
 	mousepad-search-bar.c							\
 	mousepad-search-bar.h							\
 	mousepad-statusbar.c							\
@@ -48,6 +49,8 @@ mousepad_SOURCES =								\
 	mousepad-view.h								\
 	mousepad-undo.c								\
 	mousepad-undo.h								\
+	mousepad-util.c								\
+	mousepad-util.h								\
 	mousepad-window.c							\
 	mousepad-window.h							\
 	mousepad-window-ui.h
@@ -96,14 +99,11 @@ endif
 if MAINTAINER_MODE
 CLEANFILES =									\
 	xgen-mmc								\
-	xgen-mmh								\
-	xgen-metc								\
-	xgen-meth
+	xgen-mmh
 
 DISTCLEANFILES =								\
 	$(mousepad_built_sources)                                       	\
 	stamp-mousepad-marshal.h						\
-	stamp-mousepad-enum-types.h						\
 	mousepad-window-ui.h
 
 BUILT_SOURCES =									\
@@ -118,31 +118,9 @@ endif
 mousepad-window-ui.h: Makefile $(srcdir)/mousepad-window-ui.xml
 	exo-csource --strip-comments --strip-content --static --name=mousepad_window_ui $(srcdir)/mousepad-window-ui.xml > mousepad-window-ui.h
 
-mousepad-enum-types.h: stamp-mousepad-enum-types.h
-	@true
-stamp-mousepad-enum-types.h: $(mousepad_headers) Makefile
-	( cd $(srcdir) && glib-mkenums \
-		--fhead "#ifndef __MOUSEPAD_ENUM_TYPES_H__\n#define __MOUSEPAD_ENUM_TYPES_H__\n#include <glib-object.h>\nG_BEGIN_DECLS\n" \
-		--fprod "/* enumerations from \"@filename@\" */\n" \
-		--vhead "GType @enum_name at _get_type (void) G_GNUC_CONST;\n#define MOUSEPAD_TYPE_ at ENUMSHORT@ (@enum_name at _get_type())\n" \
-		--ftail "G_END_DECLS\n\n#endif /* !__MOUSEPAD_ENUM_TYPES_H__ */" \
-		$(mousepad_headers) ) >> xgen-meth \
-	&& ( cmp -s xgen-meth mousepad-enum-types.h || cp xgen-meth mousepad-enum-types.h ) \
-	&& rm -f xgen-meth \
-	&& echo timestamp > $(@F)
-mousepad-enum-types.c: $(mousepad_headers) Makefile
-	( cd $(srcdir) && glib-mkenums \
-		--fhead "#include <mousepad/mousepad-enum-types.h>\n#include <mousepad/mousepad-types.h>" \
-		--fprod "\n/* enumerations from \"@filename@\" */" \
-		--vhead "GType\n at enum_name@_get_type (void)\n{\n\tstatic GType type = 0;\n\tif (type == 0) {\n\tstatic const G at Type@Value values[] = {"\
-		--vprod "\t{ @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
-		--vtail "\t{ 0, NULL, NULL }\n\t};\n\ttype = g_ at type@_register_static (\"@EnumName@\", values);\n  }\n\treturn type;\n}\n" \
-		$(mousepad_headers) ) >> xgen-metc \
-	&& cp xgen-metc mousepad-enum-types.c \
-	&& rm -f xgen-metc
-
 mousepad-marshal.h: stamp-mousepad-marshal.h
 	@true
+
 stamp-mousepad-marshal.h: mousepad-marshal.list Makefile
 	( cd $(srcdir) && glib-genmarshal \
 		--prefix=_mousepad_marshal \
diff --git a/mousepad/main.c b/mousepad/main.c
index 7c930e1..2780b8e 100644
--- a/mousepad/main.c
+++ b/mousepad/main.c
@@ -64,7 +64,6 @@ main (gint argc, gchar **argv)
   MousepadApplication *application;
   GError              *error = NULL;
   gchar               *working_directory;
-
 #ifdef HAVE_DBUS
   MousepadDBusService *dbus_service;
 #endif
@@ -80,24 +79,24 @@ main (gint argc, gchar **argv)
   g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
 #endif
 
-  /* initialize the GThread system */
+  /* initialize the gthread system */
   if (G_LIKELY (!g_thread_supported ()))
     g_thread_init (NULL);
 
-  /* initialize Gtk+ */
+  /* initialize gtk+ */
   if (!gtk_init_with_args (&argc, &argv, _("[FILES...]"), option_entries, GETTEXT_PACKAGE, &error))
     {
       /* check if we have an error message */
       if (G_LIKELY (error == NULL))
         {
-          /* no error message, the GUI initialization failed */
+          /* no error message, the gui initialization failed */
           g_error ("%s", _("Failed to open display."));
         }
       else
         {
-           /* print the error message */
-           g_error ("%s", error->message);
-           g_error_free (error);
+          /* print the error message */
+          g_error ("%s", error->message);
+          g_error_free (error);
         }
 
       return EXIT_FAILURE;
diff --git a/mousepad/mousepad-application.c b/mousepad/mousepad-application.c
index de0b8b7..9df01ca 100644
--- a/mousepad/mousepad-application.c
+++ b/mousepad/mousepad-application.c
@@ -21,8 +21,8 @@
 
 #include <mousepad/mousepad-private.h>
 #include <mousepad/mousepad-application.h>
-#include <mousepad/mousepad-types.h>
 #include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-replace-dialog.h>
 #include <mousepad/mousepad-window.h>
 
 
@@ -35,6 +35,8 @@ static void        mousepad_application_window_destroyed          (GtkWidget
 static GtkWidget  *mousepad_application_create_window             (MousepadApplication        *application);
 static void        mousepad_application_new_window_with_document  (MousepadWindow             *existing,
                                                                    MousepadDocument           *document,
+                                                                   gint                        x,
+                                                                   gint                        y,
                                                                    MousepadApplication        *application);
 static void        mousepad_application_new_window                (MousepadWindow             *existing,
                                                                    MousepadApplication        *application);
@@ -126,6 +128,8 @@ mousepad_application_init (MousepadApplication *application)
     {
       /* load the accel map */
       gtk_accel_map_load (path);
+
+      /* cleanup */
       g_free (path);
     }
 }
@@ -139,12 +143,19 @@ mousepad_application_finalize (GObject *object)
   GSList              *li;
   gchar               *path;
 
+  /* flush the history items of the replace dialog
+   * this is a bit of an ugly place, but cleaning on a window close
+   * isn't a good option eighter */
+  mousepad_replace_dialog_history_clean ();
+
   /* save the current accel map */
   path = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, PACKAGE_NAME G_DIR_SEPARATOR_S "accels.scm", TRUE);
   if (G_LIKELY (path != NULL))
     {
       /* save the accel map */
       gtk_accel_map_save (path);
+
+      /* cleanup */
       g_free (path);
     }
 
@@ -155,6 +166,7 @@ mousepad_application_finalize (GObject *object)
       gtk_widget_destroy (GTK_WIDGET (li->data));
     }
 
+  /* cleanup the list of windows */
   g_slist_free (application->windows);
 
   (*G_OBJECT_CLASS (mousepad_application_parent_class)->finalize) (object);
@@ -230,6 +242,8 @@ mousepad_application_create_window (MousepadApplication *application)
 static void
 mousepad_application_new_window_with_document (MousepadWindow      *existing,
                                                MousepadDocument    *document,
+                                               gint                 x,
+                                               gint                 y,
                                                MousepadApplication *application)
 {
   GtkWidget *window;
@@ -247,9 +261,13 @@ mousepad_application_new_window_with_document (MousepadWindow      *existing,
   if (G_LIKELY (screen != NULL))
     gtk_window_set_screen (GTK_WINDOW (window), screen);
 
+  /* move the window on valid cooridinates */
+  if (x > -1 && y > -1)
+    gtk_window_move (GTK_WINDOW (window), x, y);
+
   /* create an empty document if no document was send */
   if (document == NULL)
-    document = MOUSEPAD_DOCUMENT (mousepad_document_new ());
+    document = mousepad_document_new ();
 
   /* add the document to the new window */
   mousepad_window_add (MOUSEPAD_WINDOW (window), document);
@@ -265,7 +283,7 @@ mousepad_application_new_window (MousepadWindow      *existing,
                                  MousepadApplication *application)
 {
   /* trigger new document function */
-  mousepad_application_new_window_with_document (existing, NULL, application);
+  mousepad_application_new_window_with_document (existing, NULL, -1, -1, application);
 }
 
 
@@ -297,7 +315,7 @@ mousepad_application_new_window_with_files (MousepadApplication  *application,
   if (succeed == FALSE)
     {
       /* create a new document */
-      document = MOUSEPAD_DOCUMENT (mousepad_document_new ());
+      document = mousepad_document_new ();
 
       /* add the document to the new window */
       mousepad_window_add (MOUSEPAD_WINDOW (window), document);
diff --git a/mousepad/mousepad-dialogs.c b/mousepad/mousepad-dialogs.c
index 4a60757..cfb4fa6 100644
--- a/mousepad/mousepad-dialogs.c
+++ b/mousepad/mousepad-dialogs.c
@@ -21,25 +21,7 @@
 
 #include <mousepad/mousepad-private.h>
 #include <mousepad/mousepad-dialogs.h>
-#include <mousepad/mousepad-file.h>
-
-
-
-static GtkWidget *
-mousepad_dialogs_image_button (const gchar *stock_id,
-                               const gchar *label)
-{
-  GtkWidget *button, *image;
-
-  image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
-  gtk_widget_show (image);
-
-  button = gtk_button_new_with_mnemonic (label);
-  gtk_button_set_image (GTK_BUTTON (button), image);
-  gtk_widget_show (button);
-
-  return button;
-}
+#include <mousepad/mousepad-util.h>
 
 
 
@@ -48,9 +30,8 @@ mousepad_dialogs_show_about (GtkWindow *parent)
 {
   static const gchar *authors[] =
   {
-    "Benedikt Meurer <benny at xfce.org>",
-    "Erik Harrison <erikharrison at xfce.org>",
     "Nick Schermer <nick at xfce.org>",
+    "Erik Harrison <erikharrison at xfce.org>",
     NULL,
   };
 
@@ -58,7 +39,6 @@ mousepad_dialogs_show_about (GtkWindow *parent)
   gtk_show_about_dialog (parent,
                          "authors", authors,
                          "comments", _("Mousepad is a fast text editor for the Xfce Desktop Environment."),
-                         "copyright", _("Copyright \302\251 2004-2007 Xfce Development Team"),
                          "destroy-with-parent", TRUE,
                          "license", XFCE_LICENSE_GPL,
                          "logo-icon-name", PACKAGE_NAME,
@@ -117,7 +97,7 @@ mousepad_dialogs_jump_to (GtkWindow *parent,
                                         GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL,
                                         GTK_STOCK_JUMP_TO, MOUSEPAD_RESPONSE_JUMP_TO,
                                         NULL);
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_JUMP_TO);
   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
 
   hbox = gtk_hbox_new (FALSE, 8);
@@ -161,28 +141,25 @@ mousepad_dialogs_clear_recent (GtkWindow *parent)
   GtkWidget *image;
   gboolean   succeed = FALSE;
 
-  /* the dialog icon */
-  image = gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_DIALOG);
-  gtk_widget_show (image);
-
   /* create the question dialog */
   dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
-                                   _("Remove all entries from the document history?"));
-
+                                   _("Remove all entries from the documents history?"));
   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                           GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL,
                           GTK_STOCK_CLEAR, MOUSEPAD_RESPONSE_CLEAR,
                           NULL);
-
-  gtk_window_set_title (GTK_WINDOW (dialog), _("Clear Document History"));
+  gtk_window_set_title (GTK_WINDOW (dialog), _("Clear Documents History"));
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_CANCEL);
-  gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
-
   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                                            _("Clearing the document history will permanently "
+                                            _("Clearing the documents history will permanently "
                                               "remove all currently listed entries."));
 
+  /* the dialog icon */
+  image = gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_DIALOG);
+  gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
+  gtk_widget_show (image);
+
   /* popup the dialog */
   if (gtk_dialog_run (GTK_DIALOG (dialog)) == MOUSEPAD_RESPONSE_CLEAR)
     succeed = TRUE;
@@ -212,7 +189,7 @@ mousepad_dialogs_save_changes (GtkWindow *parent)
                                    _("Do you want to save the changes before closing?"));
 
   gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
-                                mousepad_dialogs_image_button (GTK_STOCK_DELETE, _("_Don't Save")),
+                                mousepad_util_image_button (GTK_STOCK_DELETE, _("_Don't Save")),
                                 MOUSEPAD_RESPONSE_DONT_SAVE);
 
   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
@@ -238,76 +215,6 @@ mousepad_dialogs_save_changes (GtkWindow *parent)
 
 
 
-static GtkFileChooserConfirmation
-mousepad_dialogs_save_as_callback (GtkFileChooser *dialog)
-{
-  gchar                      *filename;
-  GError                     *error = NULL;
-  GtkFileChooserConfirmation  result;
-
-  /* get the filename */
-  filename = gtk_file_chooser_get_filename (dialog);
-
-  if (mousepad_file_is_writable (filename, &error))
-    {
-      /* show the normal confirmation dialog */
-      result = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
-    }
-  else
-    {
-      /* tell the user he cannot write to this file */
-      mousepad_dialogs_show_error (GTK_WINDOW (dialog), error, _("Permission denied"));
-      g_error_free (error);
-
-      /* the user doesn't have permission to write to the file */
-      result = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
-    }
-
-  /* cleanup */
-  g_free (filename);
-
-  return result;
-}
-
-
-
-gchar *
-mousepad_dialogs_save_as (GtkWindow   *parent,
-                          const gchar *filename)
-{
-  gchar     *new_filename = NULL;
-  GtkWidget *dialog;
-
-  dialog = gtk_file_chooser_dialog_new (_("Save As"),
-                                        parent,
-                                        GTK_FILE_CHOOSER_ACTION_SAVE,
-                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                        GTK_STOCK_SAVE, GTK_RESPONSE_OK,
-                                        NULL);
-  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
-  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
-
-  /* we check if the user is allowed to write to the file */
-  g_signal_connect (G_OBJECT (dialog), "confirm-overwrite",
-                    G_CALLBACK (mousepad_dialogs_save_as_callback), NULL);
-
-  /* set the current filename */
-  if (filename != NULL)
-    gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), filename);
-
-  /* run the dialog */
-  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
-    new_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-
-  /* destroy the dialog */
-  gtk_widget_destroy (dialog);
-
-  return new_filename;
-}
-
-
-
 gint
 mousepad_dialogs_ask_overwrite (GtkWindow   *parent,
                                 const gchar *filename)
@@ -328,10 +235,10 @@ mousepad_dialogs_ask_overwrite (GtkWindow   *parent,
 
   gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL);
   gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
-                                mousepad_dialogs_image_button (GTK_STOCK_SAVE, _("_Overwrite")),
+                                mousepad_util_image_button (GTK_STOCK_SAVE, _("_Overwrite")),
                                 MOUSEPAD_RESPONSE_OVERWRITE);
   gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
-                                mousepad_dialogs_image_button (GTK_STOCK_REFRESH, _("_Reload")),
+                                mousepad_util_image_button (GTK_STOCK_REFRESH, _("_Reload")),
                                 MOUSEPAD_RESPONSE_RELOAD);
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_CANCEL);
 
@@ -365,7 +272,7 @@ mousepad_dialogs_ask_reload (GtkWindow *parent)
                           GTK_STOCK_SAVE_AS, MOUSEPAD_RESPONSE_SAVE_AS,
                           NULL);
   gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
-                                mousepad_dialogs_image_button (GTK_STOCK_REFRESH, _("_Reload")),
+                                mousepad_util_image_button (GTK_STOCK_REFRESH, _("_Reload")),
                                 MOUSEPAD_RESPONSE_RELOAD);
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_CANCEL);
 
diff --git a/mousepad/mousepad-dialogs.h b/mousepad/mousepad-dialogs.h
index df42011..74a7887 100644
--- a/mousepad/mousepad-dialogs.h
+++ b/mousepad/mousepad-dialogs.h
@@ -20,39 +20,44 @@
 
 G_BEGIN_DECLS
 
+/* dialog responses */
 enum {
   MOUSEPAD_RESPONSE_CANCEL,
   MOUSEPAD_RESPONSE_CLEAR,
+  MOUSEPAD_RESPONSE_CLOSE,
   MOUSEPAD_RESPONSE_DONT_SAVE,
+  MOUSEPAD_RESPONSE_FIND,
   MOUSEPAD_RESPONSE_JUMP_TO,
   MOUSEPAD_RESPONSE_OVERWRITE,
   MOUSEPAD_RESPONSE_RELOAD,
+  MOUSEPAD_RESPONSE_REPLACE,
   MOUSEPAD_RESPONSE_SAVE,
   MOUSEPAD_RESPONSE_SAVE_AS,
+  MOUSEPAD_RESPONSE_CHECK_ENTRY
 };
 
-void      mousepad_dialogs_show_about    (GtkWindow    *parent);
+GtkWidget *mousepad_dialogs_image_button (const gchar *stock_id,
+                                          const gchar *label);
 
+void       mousepad_dialogs_show_about    (GtkWindow    *parent);
 
-void      mousepad_dialogs_show_error    (GtkWindow    *parent,
-                                          const GError *error,
-                                          const gchar  *message);
 
-gint      mousepad_dialogs_jump_to       (GtkWindow    *parent,
-                                          gint          current_line,
-                                          gint          last_line);
+void       mousepad_dialogs_show_error    (GtkWindow    *parent,
+                                           const GError *error,
+                                           const gchar  *message);
 
-gboolean  mousepad_dialogs_clear_recent  (GtkWindow    *parent);
+gint       mousepad_dialogs_jump_to       (GtkWindow    *parent,
+                                           gint          current_line,
+                                           gint          last_line);
 
-gint      mousepad_dialogs_save_changes  (GtkWindow    *parent);
+gboolean   mousepad_dialogs_clear_recent  (GtkWindow    *parent);
 
-gchar    *mousepad_dialogs_save_as       (GtkWindow    *parent,
-                                          const gchar  *filename);
+gint       mousepad_dialogs_save_changes  (GtkWindow    *parent);
 
-gint      mousepad_dialogs_ask_overwrite (GtkWindow    *parent,
-                                          const gchar  *filename);
+gint       mousepad_dialogs_ask_overwrite (GtkWindow    *parent,
+                                           const gchar  *filename);
 
-gint      mousepad_dialogs_ask_reload    (GtkWindow    *parent);
+gint       mousepad_dialogs_ask_reload    (GtkWindow    *parent);
 
 G_END_DECLS
 
diff --git a/mousepad/mousepad-document.c b/mousepad/mousepad-document.c
index 0c5918d..782568a 100644
--- a/mousepad/mousepad-document.c
+++ b/mousepad/mousepad-document.c
@@ -28,32 +28,25 @@
 #ifdef HAVE_TIME_H
 #include <time.h>
 #endif
-#ifdef HAVE_CTYPE_H
-#include <ctype.h>
-#endif
 
 #include <mousepad/mousepad-private.h>
-#include <mousepad/mousepad-types.h>
+#include <mousepad/mousepad-util.h>
 #include <mousepad/mousepad-document.h>
-#include <mousepad/mousepad-file.h>
 #include <mousepad/mousepad-marshal.h>
 #include <mousepad/mousepad-view.h>
 #include <mousepad/mousepad-undo.h>
+#include <mousepad/mousepad-preferences.h>
 #include <mousepad/mousepad-window.h>
 
 
 
+#define MOUSEPAD_DOCUMENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MOUSEPAD_TYPE_DOCUMENT, MousepadDocumentPrivate))
+
+
+
 static void      mousepad_document_class_init              (MousepadDocumentClass  *klass);
 static void      mousepad_document_init                    (MousepadDocument       *document);
 static void      mousepad_document_finalize                (GObject                *object);
-static void      mousepad_document_emit_can_undo           (MousepadUndo           *undo,
-                                                            gboolean                can_undo,
-                                                            MousepadDocument       *document);
-static void      mousepad_document_emit_can_redo           (MousepadUndo           *undo,
-                                                            gboolean                can_redo,
-                                                            MousepadDocument       *document);
-static void      mousepad_document_modified_changed        (GtkTextBuffer          *buffer,
-                                                            MousepadDocument       *document);
 static void      mousepad_document_notify_has_selection    (GtkTextBuffer          *buffer,
                                                             GParamSpec             *pspec,
                                                             MousepadDocument       *document);
@@ -71,14 +64,8 @@ static void      mousepad_document_drag_data_received      (GtkWidget
                                                             guint                   info,
                                                             guint                   time,
                                                             MousepadDocument       *document);
-static void      mousepad_document_scroll_to_visible_area  (MousepadDocument       *document);
-static gboolean  mousepad_document_iter_search             (const GtkTextIter      *start,
-                                                            const gchar            *str,
-                                                            MousepadSearchFlags     flags,
-                                                            GtkTextIter            *match_start,
-                                                            GtkTextIter            *match_end,
-                                                            gboolean                forward_search);
-static void      mousepad_document_update_tab              (MousepadDocument       *document);
+static void      mousepad_document_filename_changed        (MousepadDocument       *document,
+                                                            const gchar            *filename);
 static void      mousepad_document_tab_button_clicked      (GtkWidget              *widget,
                                                             MousepadDocument       *document);
 
@@ -87,11 +74,8 @@ enum
 {
   CLOSE_TAB,
   SELECTION_CHANGED,
-  MODIFIED_CHANGED,
   CURSOR_CHANGED,
   OVERWRITE_CHANGED,
-  CAN_UNDO,
-  CAN_REDO,
   LAST_SIGNAL,
 };
 
@@ -100,37 +84,20 @@ struct _MousepadDocumentClass
   GtkScrolledWindowClass __parent__;
 };
 
-struct _MousepadDocument
+struct _MousepadDocumentPrivate
 {
-  GtkScrolledWindow  __parent__;
-
-  /* text view */
-  GtkTextView       *textview;
-  GtkTextBuffer     *buffer;
-
-  /* the undo manager */
-  MousepadUndo      *undo;
-
-  /* the highlight tag */
-  GtkTextTag        *tag;
+  GtkScrolledWindow __parent__;
 
   /* the tab label and ebox */
-  GtkWidget         *ebox;
-  GtkWidget         *label;
-
-  /* absolute path of the file */
-  gchar             *filename;
-
-  /* name of the file used for the titles */
-  gchar             *display_name;
+  GtkWidget           *ebox;
+  GtkWidget           *label;
 
-  /* last document modified time */
-  time_t             mtime;
+  /* utf-8 valid document names */
+  gchar               *utf8_filename;
+  gchar               *utf8_basename;
 
   /* settings */
-  guint              word_wrap : 1;
-  guint              line_numbers : 1;
-  guint              auto_indent : 1;
+  guint                word_wrap : 1;
 };
 
 
@@ -140,7 +107,7 @@ static guint         document_signals[LAST_SIGNAL];
 
 
 
-GtkWidget *
+MousepadDocument *
 mousepad_document_new (void)
 {
   return g_object_new (MOUSEPAD_TYPE_DOCUMENT, NULL);
@@ -174,6 +141,8 @@ mousepad_document_class_init (MousepadDocumentClass *klass)
 {
   GObjectClass *gobject_class;
 
+  g_type_class_add_private (klass, sizeof (MousepadDocumentPrivate));
+
   mousepad_document_parent_class = g_type_class_peek_parent (klass);
 
   gobject_class = G_OBJECT_CLASS (klass);
@@ -182,7 +151,7 @@ mousepad_document_class_init (MousepadDocumentClass *klass)
   document_signals[CLOSE_TAB] =
     g_signal_new (I_("close-tab"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
@@ -190,23 +159,15 @@ mousepad_document_class_init (MousepadDocumentClass *klass)
   document_signals[SELECTION_CHANGED] =
     g_signal_new (I_("selection-changed"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__BOOLEAN,
                   G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
 
-  document_signals[MODIFIED_CHANGED] =
-    g_signal_new (I_("modified-changed"),
-                  G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  g_cclosure_marshal_VOID__VOID,
-                  G_TYPE_NONE, 0);
-
   document_signals[CURSOR_CHANGED] =
     g_signal_new (I_("cursor-changed"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   _mousepad_marshal_VOID__UINT_UINT,
                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
@@ -214,23 +175,7 @@ mousepad_document_class_init (MousepadDocumentClass *klass)
   document_signals[OVERWRITE_CHANGED] =
     g_signal_new (I_("overwrite-changed"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  g_cclosure_marshal_VOID__BOOLEAN,
-                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
-  document_signals[CAN_UNDO] =
-    g_signal_new (I_("can-undo"),
-                  G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  g_cclosure_marshal_VOID__BOOLEAN,
-                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
-  document_signals[CAN_REDO] =
-    g_signal_new (I_("can-redo"),
-                  G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__BOOLEAN,
                   G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
@@ -241,12 +186,18 @@ mousepad_document_class_init (MousepadDocumentClass *klass)
 static void
 mousepad_document_init (MousepadDocument *document)
 {
-  GtkTargetList *target_list;
+  GtkTargetList       *target_list;
+  gboolean             word_wrap, auto_indent, line_numbers, insert_spaces;
+  gchar               *font_name;
+  gint                 tab_width;
+  MousepadPreferences *preferences;
+
+  /* private structure */
+  document->priv = MOUSEPAD_DOCUMENT_GET_PRIVATE (document);
 
   /* initialize the variables */
-  document->filename     = NULL;
-  document->display_name = NULL;
-  document->mtime        = 0;
+  document->priv->utf8_filename = NULL;
+  document->priv->utf8_basename = NULL;
 
   /* setup the scolled window */
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (document), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
@@ -257,15 +208,15 @@ mousepad_document_init (MousepadDocument *document)
   /* create a textbuffer */
   document->buffer = gtk_text_buffer_new (NULL);
 
+  /* initialize the file */
+  document->file = mousepad_file_new (document->buffer);
+
+  /* connect signals to the file */
+  g_signal_connect_swapped (G_OBJECT (document->file), "filename-changed", G_CALLBACK (mousepad_document_filename_changed), document);
+
   /* initialize the undo manager */
   document->undo = mousepad_undo_new (document->buffer);
 
-  /* connect signals to the undo manager */
-  g_signal_connect (G_OBJECT (document->undo), "can-undo",
-                    G_CALLBACK (mousepad_document_emit_can_undo), document);
-  g_signal_connect (G_OBJECT (document->undo), "can-redo",
-                    G_CALLBACK (mousepad_document_emit_can_redo), document);
-
   /* create the highlight tag */
   document->tag = gtk_text_buffer_create_tag (document->buffer, NULL, "background", "#ffff78", NULL);
 
@@ -278,8 +229,34 @@ mousepad_document_init (MousepadDocument *document)
   target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (document->textview));
   gtk_target_list_add_table (target_list, drop_targets, G_N_ELEMENTS (drop_targets));
 
+  /* preferences */
+  preferences = mousepad_preferences_get ();
+
+  /* read all the default settings */
+  g_object_get (G_OBJECT (preferences),
+                "view-word-wrap", &word_wrap,
+                "view-line-numbers", &line_numbers,
+                "view-auto-indent", &auto_indent,
+                "view-font-name", &font_name,
+                "view-tab-width", &tab_width,
+                "view-insert-spaces", &insert_spaces,
+                NULL);
+
+  /* release the preferences */
+  g_object_unref (G_OBJECT (preferences));
+
+  /* set all the settings */
+  mousepad_document_set_word_wrap (document, word_wrap);
+  mousepad_document_set_font (document, font_name);
+  mousepad_view_set_line_numbers (document->textview, line_numbers);
+  mousepad_view_set_auto_indent (document->textview, auto_indent);
+  mousepad_view_set_tab_width (document->textview, tab_width);
+  mousepad_view_set_insert_spaces (document->textview, insert_spaces);
+
+  /* cleanup */
+  g_free (font_name);
+
   /* attach signals to the text view and buffer */
-  g_signal_connect (G_OBJECT (document->buffer), "modified-changed", G_CALLBACK (mousepad_document_modified_changed), document);
   g_signal_connect (G_OBJECT (document->buffer), "notify::has-selection", G_CALLBACK (mousepad_document_notify_has_selection), document);
   g_signal_connect (G_OBJECT (document->buffer), "notify::cursor-position", G_CALLBACK (mousepad_document_notify_cursor_position), document);
   g_signal_connect (G_OBJECT (document->textview), "notify::overwrite", G_CALLBACK (mousepad_document_toggle_overwrite), document);
@@ -295,58 +272,22 @@ mousepad_document_finalize (GObject *object)
   MousepadDocument *document = MOUSEPAD_DOCUMENT (object);
 
   /* cleanup */
-  g_free (document->filename);
-  g_free (document->display_name);
+  g_free (document->priv->utf8_filename);
+  g_free (document->priv->utf8_basename);
 
   /* release the undo manager */
   g_object_unref (G_OBJECT (document->undo));
 
-  /* release our reference from the buffer */
+  /* release the file */
+  g_object_unref (G_OBJECT (document->file));
+
+  /* release the buffer reference */
   g_object_unref (G_OBJECT (document->buffer));
 
   (*G_OBJECT_CLASS (mousepad_document_parent_class)->finalize) (object);
 }
 
 
-
-static void
-mousepad_document_emit_can_undo (MousepadUndo     *undo,
-                                 gboolean          can_undo,
-                                 MousepadDocument *document)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-  /* emit the signal */
-  g_signal_emit (G_OBJECT (document), document_signals[CAN_UNDO], 0, can_undo);
-}
-
-
-
-static void
-mousepad_document_emit_can_redo (MousepadUndo     *undo,
-                                 gboolean          can_redo,
-                                 MousepadDocument *document)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-  /* emit the signal */
-  g_signal_emit (G_OBJECT (document), document_signals[CAN_REDO], 0, can_redo);
-}
-
-
-
-static void
-mousepad_document_modified_changed (GtkTextBuffer    *buffer,
-                                    MousepadDocument *document)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-  /* emit the signal */
-  g_signal_emit (G_OBJECT (document), document_signals[MODIFIED_CHANGED], 0);
-}
-
-
-
 static void
 mousepad_document_notify_has_selection (GtkTextBuffer    *buffer,
                                         GParamSpec       *pspec,
@@ -358,7 +299,7 @@ mousepad_document_notify_has_selection (GtkTextBuffer    *buffer,
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
 
   /* check if we have selected text or not */
-  has_selection = mousepad_document_get_has_selection (document);
+  has_selection = mousepad_view_get_has_selection (document->textview);
 
   /* emit the signal */
   g_signal_emit (G_OBJECT (document), document_signals[SELECTION_CHANGED], 0, has_selection);
@@ -371,8 +312,9 @@ mousepad_document_notify_cursor_position (GtkTextBuffer    *buffer,
                                           GParamSpec       *pspec,
                                           MousepadDocument *document)
 {
-  GtkTextIter iter, start;
+  GtkTextIter iter;
   guint       line, column = 0;
+  gint        tab_width;
 
   _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
@@ -380,25 +322,17 @@ mousepad_document_notify_cursor_position (GtkTextBuffer    *buffer,
   /* get the current iter position */
   gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
 
-  /* get the current line numbr */
+  /* get the current line number */
   line = gtk_text_iter_get_line (&iter) + 1;
 
-  /* get the column */
-  start = iter;
-  gtk_text_iter_set_line_offset (&start, 0);
-
-  while (!gtk_text_iter_equal (&start, &iter))
-    {
-      if (gtk_text_iter_get_char (&start) == '\t')
-        column += (8 - (column % 8));
-      else
-        ++column;
+  /* get the tab width */
+  tab_width = mousepad_view_get_tab_width (document->textview);
 
-      gtk_text_iter_forward_char (&start);
-    }
+  /* get the column */
+  column = mousepad_util_get_real_line_offset (&iter, tab_width) + 1;
 
   /* emit the signal */
-  g_signal_emit (G_OBJECT (document), document_signals[CURSOR_CHANGED], 0, line, column + 1);
+  g_signal_emit (G_OBJECT (document), document_signals[CURSOR_CHANGED], 0, line, column);
 }
 
 
@@ -442,158 +376,40 @@ mousepad_document_drag_data_received (GtkWidget        *widget,
 
 
 static void
-mousepad_document_scroll_to_visible_area (MousepadDocument *document)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-  /* scroll to visible area */
-  gtk_text_view_scroll_to_mark (document->textview,
-                                gtk_text_buffer_get_insert (document->buffer),
-                                0.02, FALSE, 0.0, 0.0);
-}
-
-
-
-gboolean
-mousepad_document_reload (MousepadDocument  *document,
-                          GError           **error)
-{
-  GtkTextIter  start, end;
-  gchar       *filename;
-  gboolean     succeed = FALSE;
-
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-  _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-  /* remove the content of the textview */
-  gtk_text_buffer_get_bounds (document->buffer, &start, &end);
-  gtk_text_buffer_delete (document->buffer, &start, &end);
-
-  /* we have to copy the filename, because mousepad_screen_open_file (resets) the name */
-  filename = g_strdup (document->filename);
-
-  /* reload the document */
-  succeed = mousepad_document_open_file (document, filename, error);
-
-  /* cleanup */
-  g_free (filename);
-
-  return succeed;
-}
-
-
-
-gboolean
-mousepad_document_save_file (MousepadDocument  *document,
-                             const gchar       *filename,
-                             GError           **error)
+mousepad_document_filename_changed (MousepadDocument *document,
+                                    const gchar      *filename)
 {
-  gchar       *content;
-  gchar       *converted = NULL;
-  GtkTextIter  start, end;
-  gsize        bytes;
-  gint         new_mtime;
-  gboolean     succeed = FALSE;
+  gchar *utf8_filename, *utf8_basename;
 
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-  _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  _mousepad_return_val_if_fail (filename != NULL, FALSE);
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+  _mousepad_return_if_fail (filename != NULL);
 
-  /* get the textview content */
-  gtk_text_buffer_get_bounds (document->buffer, &start, &end);
-  content = gtk_text_buffer_get_slice (document->buffer, &start, &end, TRUE);
+  /* convert the title into a utf-8 valid version for display */
+  utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
 
-  if (G_LIKELY (content != NULL))
+  if (G_LIKELY (utf8_filename))
     {
-      /* TODO: fix the file encoding here, basic utf8 for testing */
-      converted = g_convert (content,
-                             strlen (content),
-                             "UTF-8",
-                             "UTF-8",
-                             NULL,
-                             &bytes,
-                             error);
-
-      /* cleanup */
-      g_free (content);
-
-      if (G_LIKELY (converted != NULL))
-        {
-          succeed = mousepad_file_save_data (filename, converted, bytes,
-                                             &new_mtime, error);
+      /* create the display name */
+      utf8_basename = g_filename_display_basename (utf8_filename);
 
-          /* cleanup */
-          g_free (converted);
+      /* remove the old names */
+      g_free (document->priv->utf8_filename);
+      g_free (document->priv->utf8_basename);
 
-          /* saving work w/o problems */
-          if (G_LIKELY (succeed))
-            {
-              /* set the new mtime */
-              document->mtime = new_mtime;
+      /* set the new names */
+      document->priv->utf8_filename = utf8_filename;
+      document->priv->utf8_basename = utf8_basename;
 
-              /* nothing happend */
-              gtk_text_buffer_set_modified (document->buffer, FALSE);
-
-              /* we were allowed to write */
-              gtk_text_view_set_editable (document->textview, TRUE);
+      /* update the tab label and tooltip */
+      if (G_UNLIKELY (document->priv->label))
+        {
+          /* set the tab label */
+          gtk_label_set_text (GTK_LABEL (document->priv->label), utf8_basename);
 
-              /* emit the modified-changed signal */
-              mousepad_document_modified_changed (NULL, document);
-            }
+          /* set the tab tooltip */
+          mousepad_util_set_tooltip (document->priv->ebox, utf8_filename);
         }
     }
-
-  return succeed;
-}
-
-
-
-void
-mousepad_document_set_filename (MousepadDocument *document,
-                                const gchar      *filename)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-  _mousepad_return_if_fail (filename != NULL);
-
-  /* cleanup the old names */
-  g_free (document->filename);
-  g_free (document->display_name);
-
-  /* create the new names */
-  document->filename = g_strdup (filename);
-  document->display_name = g_filename_display_basename (filename);
-
-  /* update the tab label and tooltip */
-  if (document->ebox && document->label)
-    mousepad_document_update_tab (document);
-}
-
-
-
-void
-mousepad_document_set_auto_indent (MousepadDocument *document,
-                                   gboolean          auto_indent)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-  /* store the setting */
-  document->auto_indent = auto_indent;
-
-  mousepad_view_set_auto_indent (MOUSEPAD_VIEW (document->textview), auto_indent);
-}
-
-
-
-void
-mousepad_document_set_line_numbers (MousepadDocument *document,
-                                    gboolean          line_numbers)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-  /* store the setting */
-  document->line_numbers = line_numbers;
-
-  mousepad_view_set_show_line_numbers (MOUSEPAD_VIEW (document->textview), line_numbers);
 }
 
 
@@ -604,7 +420,7 @@ mousepad_document_set_overwrite (MousepadDocument *document,
 {
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
 
-  gtk_text_view_set_overwrite (document->textview, overwrite);
+  gtk_text_view_set_overwrite (GTK_TEXT_VIEW (document->textview), overwrite);
 }
 
 
@@ -616,10 +432,10 @@ mousepad_document_set_word_wrap (MousepadDocument *document,
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
 
   /* store the setting */
-  document->word_wrap = word_wrap;
+  document->priv->word_wrap = word_wrap;
 
   /* set the wrapping mode */
-  gtk_text_view_set_wrap_mode (document->textview,
+  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (document->textview),
                                word_wrap ? GTK_WRAP_WORD : GTK_WRAP_NONE);
 }
 
@@ -635,6 +451,7 @@ mousepad_document_set_font (MousepadDocument *document,
 
   if (G_LIKELY (font_name))
     {
+      /* set the widget font */
       font_desc = pango_font_description_from_string (font_name);
       gtk_widget_modify_font (GTK_WIDGET (document->textview), font_desc);
       pango_font_description_free (font_desc);
@@ -643,417 +460,6 @@ mousepad_document_set_font (MousepadDocument *document,
 
 
 
-gboolean
-mousepad_document_open_file (MousepadDocument  *document,
-                             const gchar       *filename,
-                             GError           **error)
-{
-  GtkTextIter iter;
-  gboolean    succeed = FALSE;
-  gboolean    readonly;
-  gint        new_mtime;
-
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-  _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  _mousepad_return_val_if_fail (filename != NULL, FALSE);
-
-  /* we're going to add the file content */
-  gtk_text_buffer_begin_user_action (document->buffer);
-
-  /* lock the undo manager */
-  mousepad_undo_lock (document->undo);
-
-  /* insert the file content */
-  if (mousepad_file_read_to_buffer (filename,
-                                    document->buffer,
-                                    &new_mtime,
-                                    &readonly,
-                                    error))
-    {
-      /* set the new filename */
-      mousepad_document_set_filename (document, filename);
-
-      /* set the new mtime */
-      document->mtime = new_mtime;
-
-      /* whether the textview is editable */
-      gtk_text_view_set_editable (document->textview, !readonly);
-
-      /* move the cursors to the first place and pretend nothing happend */
-      gtk_text_buffer_get_start_iter (document->buffer, &iter);
-      gtk_text_buffer_place_cursor (document->buffer, &iter);
-      gtk_text_buffer_set_modified (document->buffer, FALSE);
-      gtk_text_view_scroll_to_iter (document->textview, &iter, 0, FALSE, 0, 0);
-
-      /* it worked out very well */
-      succeed = TRUE;
-    }
-
-  /* unlock the undo manager */
-  mousepad_undo_unlock (document->undo);
-
-  /* and we're done */
-  gtk_text_buffer_end_user_action (document->buffer);
-
-  return succeed;
-}
-
-
-
-
-static gboolean
-mousepad_document_iter_search (const GtkTextIter   *start,
-                               const gchar         *str,
-                               MousepadSearchFlags  flags,
-                               GtkTextIter         *match_start,
-                               GtkTextIter         *match_end,
-                               gboolean             search_forward)
-{
-  GtkTextIter iter, begin;
-  gunichar    iter_char, str_char;
-  gboolean    succeed = FALSE;
-  gboolean    continue_search;
-  guint       str_offset = 0;
-
-  _mousepad_return_val_if_fail (start != NULL, FALSE);
-
-  /* set the start iter */
-  iter = *start;
-
-  /* walk from the start to the end iter */
-  do
-    {
-      /* get the characters we're going to compare */
-      iter_char = gtk_text_iter_get_char (&iter);
-      str_char  = g_utf8_get_char (str);
-
-      /* convert the characters to lower case if needed */
-      if (flags & MOUSEPAD_SEARCH_CASE_INSENSITIVE)
-        {
-          iter_char = tolower (iter_char);
-          str_char  = tolower (str_char);
-        }
-
-      /* compare the two characters */
-      if (iter_char == str_char)
-        {
-          /* first character matched, set the begin iter */
-          if (str_offset == 0)
-            begin = iter;
-
-          /* get the next character and increase the offset counter */
-          str = g_utf8_next_char (str);
-          str_offset++;
-
-          /* we've hit the end of the search string, so we had a full match */
-          if (G_UNLIKELY (*str == '\0'))
-            {
-              if (G_LIKELY (search_forward))
-                {
-                  /* forward one character */
-                  gtk_text_iter_forward_char (&iter);
-
-                  /* check if we match a whole word */
-                  if (flags & MOUSEPAD_SEARCH_WHOLE_WORD
-                      && !(gtk_text_iter_starts_word (&begin)
-                           && gtk_text_iter_ends_word (&iter)))
-                    goto reset_match;
-                }
-              else
-                {
-                  /* 'backward' one character */
-                  gtk_text_iter_forward_char (&begin);
-
-                  /* check if we match a whole word */
-                  if (flags & MOUSEPAD_SEARCH_WHOLE_WORD
-                      && !(gtk_text_iter_starts_word (&iter)
-                           && gtk_text_iter_ends_word (&begin)))
-                    goto reset_match;
-                }
-
-              /* set the start and end iters */
-              *match_start = begin;
-              *match_end   = iter;
-
-              /* return true and break the loop */
-              succeed = TRUE;
-              break;
-            }
-        }
-      else if (G_UNLIKELY (str_offset > 0))
-        {
-          reset_match:
-          /* go back to the first character in the string */
-          for (;str_offset > 0; str_offset--)
-            str = g_utf8_prev_char (str);
-
-          /* reset the iter */
-          iter = begin;
-        }
-
-      /* jump to next iter in the buffer */
-      if (G_LIKELY (search_forward))
-        continue_search = gtk_text_iter_forward_char (&iter);
-      else
-        continue_search = gtk_text_iter_backward_char (&iter);
-    }
-  while (G_LIKELY (continue_search));
-
-  return succeed;
-}
-
-
-
-gboolean
-mousepad_document_find (MousepadDocument    *document,
-                        const gchar         *string,
-                        MousepadSearchFlags  flags)
-{
-  gboolean     found;
-  gboolean     already_wrapped = FALSE;
-  gchar       *reversed = NULL;
-  GtkTextIter  doc_start, doc_end;
-  GtkTextIter  sel_start, sel_end;
-  GtkTextIter  match_start, match_end;
-  GtkTextIter  start;
-
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (document->buffer), FALSE);
-  _mousepad_return_val_if_fail (string && g_utf8_validate (string, -1, NULL), FALSE);
-
-  /* get the bounds */
-  gtk_text_buffer_get_bounds (document->buffer, &doc_start, &doc_end);
-  gtk_text_buffer_get_selection_bounds (document->buffer, &sel_start, &sel_end);
-
-  /* set the correct starting point for the search */
-  if (flags & MOUSEPAD_SEARCH_FORWARDS)
-    {
-      start = sel_end;
-    }
-  else
-    {
-      start = sel_start;
-
-      if (flags & MOUSEPAD_SEARCH_BACKWARDS)
-        {
-          /* the character is right of the iter, go one iter backwards */
-          gtk_text_iter_backward_char (&start);
-
-          /* reverse the search string */
-          reversed = g_utf8_strreverse (string, -1);
-        }
-    }
-
-search:
-  /* try to find the next occurence of the string */
-  if (flags & MOUSEPAD_SEARCH_BACKWARDS)
-    found = mousepad_document_iter_search (&start, reversed, flags, &match_start, &match_end, FALSE);
-  else
-    found = mousepad_document_iter_search (&start, string, flags, &match_start, &match_end, TRUE);
-
-  /* select the occurence */
-  if (found)
-    {
-      /* set the cursor in from of the matched iter */
-      gtk_text_buffer_place_cursor (document->buffer, &match_start);
-
-      /* select the match */
-      gtk_text_buffer_move_mark_by_name (document->buffer, "insert", &match_end);
-
-      /* scroll document so the cursor is visible */
-      mousepad_document_scroll_to_visible_area (document);
-    }
-  /* wrap around */
-  else if (flags & MOUSEPAD_SEARCH_WRAP_AROUND && already_wrapped == FALSE)
-    {
-      /* set the new start and end iter */
-      if (flags & MOUSEPAD_SEARCH_BACKWARDS)
-        start = doc_end;
-      else
-        start = doc_start;
-
-      /* set we did the wrap, so we don't end up in a loop */
-      already_wrapped = TRUE;
-
-      /* search again */
-      goto search;
-    }
-  else if (flags & MOUSEPAD_SEARCH_WRAP_AROUND)
-    {
-      /* nothing found, we already did the wrap, so just place the cursor where we started */
-      gtk_text_buffer_place_cursor (document->buffer, &sel_start);
-    }
-
-  /* cleanup */
-  g_free (reversed);
-
-  return found;
-}
-
-
-
-void
-mousepad_document_replace (MousepadDocument *document)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-
-}
-
-
-
-void
-mousepad_document_highlight_all (MousepadDocument    *document,
-                                 const gchar         *string,
-                                 MousepadSearchFlags  flags)
-{
-  GtkTextIter iter;
-  GtkTextIter doc_start, doc_end;
-  GtkTextIter match_start, match_end;
-  gboolean    found;
-
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-  _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (document->buffer));
-  _mousepad_return_if_fail (string ? g_utf8_validate (string, -1, NULL) : TRUE);
-
-  /* get the document bounds */
-  gtk_text_buffer_get_bounds (document->buffer, &doc_start, &doc_end);
-
-  /* remove all the highlight tags */
-  gtk_text_buffer_remove_tag (document->buffer, document->tag, &doc_start, &doc_end);
-
-  /* highlight the new string */
-  if (G_LIKELY (string != NULL && *string != '\0'))
-    {
-      /* set the iter to the beginning of the document */
-      iter = doc_start;
-
-      /* highlight all the occurences of the strings */
-      do
-        {
-          /* search for the next occurence of the string */
-          found = mousepad_document_iter_search (&iter, string, flags, &match_start, &match_end, TRUE);
-
-          if (G_LIKELY (found))
-            {
-              /* highlight the found occurence */
-              gtk_text_buffer_apply_tag (document->buffer, document->tag, &match_start, &match_end);
-
-              /* jump to the end of the highlighted string and continue searching */
-              iter = match_end;
-            }
-        }
-      while (found);
-    }
-}
-
-
-
-void
-mousepad_document_cut_selection (MousepadDocument *document)
-{
-  GtkClipboard *clipboard;
-  MousepadView *view = MOUSEPAD_VIEW (document->textview);
-
-  /* get the clipboard */
-  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (document->textview), GDK_SELECTION_CLIPBOARD);
-
-  /* cut the text */
-  if (mousepad_view_get_vertical_selection (view))
-    mousepad_view_cut_clipboard (view, clipboard);
-  else
-    gtk_text_buffer_cut_clipboard (document->buffer, clipboard, gtk_text_view_get_editable (document->textview));
-
-  /* make sure the cursor is in the visible area */
-  mousepad_document_scroll_to_visible_area (document);
-}
-
-
-
-void
-mousepad_document_copy_selection (MousepadDocument *document)
-{
-  GtkClipboard *clipboard;
-  MousepadView *view = MOUSEPAD_VIEW (document->textview);
-
-  /* get the clipboard */
-  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (document->textview), GDK_SELECTION_CLIPBOARD);
-
-  /* copy the selected text */
-  if (mousepad_view_get_vertical_selection (view))
-    mousepad_view_copy_clipboard (view, clipboard);
-  else
-    gtk_text_buffer_copy_clipboard (document->buffer, clipboard);
-}
-
-
-
-void
-mousepad_document_paste_clipboard (MousepadDocument *document)
-{
-  GtkClipboard *clipboard;
-
-  /* get the clipboard */
-  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (document->textview), GDK_SELECTION_CLIPBOARD);
-
-  /* paste the clipboard content */
-  gtk_text_buffer_paste_clipboard (document->buffer, clipboard, NULL, gtk_text_view_get_editable (document->textview));
-
-  /* make sure the cursor is in the visible area */
-  mousepad_document_scroll_to_visible_area (document);
-}
-
-
-
-void
-mousepad_document_paste_column_clipboard (MousepadDocument *document)
-{
-  GtkClipboard *clipboard;
-  MousepadView *view = MOUSEPAD_VIEW (document->textview);
-
-  /* get the clipboard */
-  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (document->textview), GDK_SELECTION_CLIPBOARD);
-
-  /* past the clipboard text in a column */
-  mousepad_view_paste_column_clipboard (view, clipboard);
-
-  /* make sure the cursor is in the visible area */
-  mousepad_document_scroll_to_visible_area (document);
-}
-
-
-
-void
-mousepad_document_delete_selection (MousepadDocument *document)
-{
-  MousepadView *view = MOUSEPAD_VIEW (document->textview);
-
-  /* delete the selected text */
-  if (mousepad_view_get_vertical_selection (view))
-    mousepad_view_delete_selection (view);
-  else
-    gtk_text_buffer_delete_selection (document->buffer, TRUE, gtk_text_view_get_editable (document->textview));
-
-  /* make sure the cursor is in the visible area */
-  mousepad_document_scroll_to_visible_area (document);
-}
-
-
-
-void
-mousepad_document_select_all (MousepadDocument *document)
-{
-  GtkTextIter start, end;
-
-  /* get the start and end iter */
-  gtk_text_buffer_get_bounds (document->buffer, &start, &end);
-
-  /* select everything between those iters */
-  gtk_text_buffer_select_range (document->buffer, &start, &end);
-}
-
-
-
 void
 mousepad_document_focus_textview (MousepadDocument *document)
 {
@@ -1072,13 +478,14 @@ mousepad_document_jump_to_line (MousepadDocument *document,
   GtkTextIter iter;
 
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+  _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (document->buffer));
 
   /* move the cursor */
-  gtk_text_buffer_get_iter_at_line (document->buffer, &iter, line_number-1);
+  gtk_text_buffer_get_iter_at_line (document->buffer, &iter, line_number - 1);
   gtk_text_buffer_place_cursor (document->buffer, &iter);
 
   /* make sure the cursor is in the visible area */
-  mousepad_document_scroll_to_visible_area (document);
+  mousepad_view_put_cursor_on_screen (document->textview);
 }
 
 
@@ -1092,7 +499,7 @@ mousepad_document_send_statusbar_signals (MousepadDocument *document)
   mousepad_document_notify_cursor_position (document->buffer, NULL, document);
 
   /* re-send the overwrite signal */
-  mousepad_document_toggle_overwrite (document->textview, NULL, document);
+  mousepad_document_toggle_overwrite (GTK_TEXT_VIEW (document->textview), NULL, document);
 }
 
 
@@ -1105,6 +512,7 @@ mousepad_document_line_numbers (MousepadDocument *document,
   GtkTextIter iter;
 
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+  _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (document->buffer));
 
   /* get the current line number */
   gtk_text_buffer_get_iter_at_mark (document->buffer, &iter, gtk_text_buffer_get_insert (document->buffer));
@@ -1118,79 +526,12 @@ mousepad_document_line_numbers (MousepadDocument *document,
 
 
 gboolean
-mousepad_document_get_externally_modified (MousepadDocument *document)
-{
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-  _mousepad_return_val_if_fail (document->filename != NULL, FALSE);
-
-  /* return whether the file has been externally modified */
-  return mousepad_file_get_externally_modified (document->filename, document->mtime);
-}
-
-
-
-gboolean
-mousepad_document_get_can_undo (MousepadDocument *document)
-{
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-
-  return mousepad_undo_can_undo (document->undo);
-}
-
-
-
-gboolean
-mousepad_document_get_can_redo (MousepadDocument *document)
-{
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-
-  return mousepad_undo_can_redo (document->undo);
-}
-
-
-
-const gchar *
-mousepad_document_get_filename (MousepadDocument *document)
-{
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), NULL);
-
-  return document->filename;
-}
-
-
-
-gboolean
-mousepad_document_get_has_selection (MousepadDocument *document)
-{
-  gboolean       has_selection;
-
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-
-  /* check if we have selected text or not */
-  g_object_get (G_OBJECT (document->buffer), "has-selection", &has_selection, NULL);
-
-  return has_selection;
-}
-
-
-
-gboolean
-mousepad_document_get_modified (MousepadDocument *document)
-{
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-
-  /* return whether the buffer has been modified */
-  return gtk_text_buffer_get_modified (document->buffer);
-}
-
-
-
-gboolean
 mousepad_document_get_readonly (MousepadDocument *document)
 {
   _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_VIEW (document->textview), FALSE);
 
-  return !gtk_text_view_get_editable (document->textview);
+  return !gtk_text_view_get_editable (GTK_TEXT_VIEW (document->textview));
 }
 
 
@@ -1206,19 +547,15 @@ mousepad_document_get_tab_label (MousepadDocument *document)
   gtk_widget_show (hbox);
 
   /* the ebox */
-  document->ebox = g_object_new (GTK_TYPE_EVENT_BOX, "border-width", 2, "visible-window", FALSE, NULL);
-  gtk_box_pack_start (GTK_BOX (hbox), document->ebox, TRUE, TRUE, 0);
-  gtk_widget_show (document->ebox);
+  document->priv->ebox = g_object_new (GTK_TYPE_EVENT_BOX, "border-width", 2, "visible-window", FALSE, NULL);
+  gtk_box_pack_start (GTK_BOX (hbox), document->priv->ebox, TRUE, TRUE, 0);
+  mousepad_util_set_tooltip (document->priv->ebox, document->priv->utf8_filename);
+  gtk_widget_show (document->priv->ebox);
 
   /* create the label */
-  document->label = g_object_new (GTK_TYPE_LABEL,
-                                 "selectable", FALSE,
-                                 "xalign", 0.0, NULL);
-  gtk_container_add (GTK_CONTAINER (document->ebox), document->label);
-  gtk_widget_show (document->label);
-
-  /* update the tab and add signal to the ebox for a title update */
-  mousepad_document_update_tab (document);
+  document->priv->label = gtk_label_new (mousepad_document_get_basename (document));
+  gtk_container_add (GTK_CONTAINER (document->priv->ebox), document->priv->label);
+  gtk_widget_show (document->priv->label);
 
   /* create the button */
   button = g_object_new (GTK_TYPE_BUTTON,
@@ -1227,10 +564,9 @@ mousepad_document_get_tab_label (MousepadDocument *document)
                          "border-width", 0,
                          "can-default", FALSE,
                          "can-focus", FALSE, NULL);
-  mousepad_gtk_set_tooltip (button, _("Close this tab"));
+  mousepad_util_set_tooltip (button, _("Close this tab"));
   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
-  g_signal_connect (G_OBJECT (button), "clicked",
-                    G_CALLBACK (mousepad_document_tab_button_clicked), document);
+  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (mousepad_document_tab_button_clicked), document);
   gtk_widget_show (button);
 
   /* button image */
@@ -1244,19 +580,6 @@ mousepad_document_get_tab_label (MousepadDocument *document)
 
 
 static void
-mousepad_document_update_tab (MousepadDocument *document)
-{
-  /* set the tab label */
-  gtk_label_set_text (GTK_LABEL (document->label),
-                      mousepad_document_get_title (document, FALSE));
-
-  /* set the tab tooltip */
-  mousepad_gtk_set_tooltip (document->ebox, document->filename);
-}
-
-
-
-static void
 mousepad_document_tab_button_clicked (GtkWidget        *widget,
                                       MousepadDocument *document)
 {
@@ -1266,86 +589,71 @@ mousepad_document_tab_button_clicked (GtkWidget        *widget,
 
 
 const gchar *
-mousepad_document_get_title (MousepadDocument *document,
-                             gboolean          show_full_path)
+mousepad_document_get_basename (MousepadDocument *document)
 {
-  const gchar  *title;
-  static guint  untitled_counter = 0;
+  static gint untitled_counter = 0;
 
   _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), NULL);
 
-  if (G_UNLIKELY (show_full_path && document->filename))
+  /* check if there is a filename set */
+  if (document->priv->utf8_basename == NULL)
     {
-      /* return the filename */
-      title = document->filename;
-    }
-  else
-    {
-      /* check if the document is still untitled, if so, fix it */
-      if (G_UNLIKELY (document->display_name == NULL))
-        document->display_name = g_strdup_printf ("%s %d", _("Untitled"), ++untitled_counter);
-
-      /* return the display_name */
-      title = document->display_name;
+      /* create an unique untitled document name */
+      document->priv->utf8_basename = g_strdup_printf ("%s %d", _("Untitled"), ++untitled_counter);
     }
 
-  return title;
+  return document->priv->utf8_basename;
 }
 
 
 
-gboolean
-mousepad_document_get_word_wrap (MousepadDocument *document)
-{
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
-
-  return document->word_wrap;
-}
-
-
-
-gboolean
-mousepad_document_get_line_numbers (MousepadDocument *document)
+const gchar *
+mousepad_document_get_filename (MousepadDocument *document)
 {
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), NULL);
 
-  return document->line_numbers;
+  return document->priv->utf8_filename;
 }
 
 
 
 gboolean
-mousepad_document_get_auto_indent (MousepadDocument *document)
+mousepad_document_get_word_wrap (MousepadDocument *document)
 {
   _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
 
-  return document->auto_indent;
+  return document->priv->word_wrap;
 }
 
 
 
 void
-mousepad_document_undo (MousepadDocument *document)
+mousepad_document_get_font_information (MousepadDocument      *document,
+                                        PangoFontDescription **font_desc,
+                                        gint                  *font_height)
 {
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
-
-  /* undo */
-  mousepad_undo_do_undo (document->undo);
+  PangoContext         *context;
+  PangoFontMetrics     *metrics;
+  PangoFontDescription *font;
 
-  /* scroll to visible area */
-  mousepad_document_scroll_to_visible_area (document);
-}
+  /* get the textview context */
+  context = gtk_widget_get_pango_context (GTK_WIDGET (document->textview));
 
+  /* get the font description */
+  font = pango_context_get_font_description (context);
 
+  if (font_desc)
+    *font_desc = font;
 
-void
-mousepad_document_redo (MousepadDocument *document)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+  if (G_LIKELY (font_height))
+    {
+      /* get metric information about the font */
+      metrics = pango_context_get_metrics (context, font, pango_context_get_language (context));
 
-  /* redo */
-  mousepad_undo_do_redo (document->undo);
+      /* calculate the real font height */
+      *font_height = (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)) / PANGO_SCALE;
 
-  /* scroll to visible area */
-  mousepad_document_scroll_to_visible_area (document);
+      /* release the metrics */
+      pango_font_metrics_unref (metrics);
+    }
 }
diff --git a/mousepad/mousepad-document.h b/mousepad/mousepad-document.h
index ff7b569..d9af13d 100644
--- a/mousepad/mousepad-document.h
+++ b/mousepad/mousepad-document.h
@@ -20,10 +20,14 @@
 
 G_BEGIN_DECLS
 
-#include <mousepad/mousepad-types.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-file.h>
+#include <mousepad/mousepad-undo.h>
+#include <mousepad/mousepad-view.h>
 
-typedef struct _MousepadDocumentClass MousepadDocumentClass;
-typedef struct _MousepadDocument      MousepadDocument;
+typedef struct _MousepadDocumentPrivate MousepadDocumentPrivate;
+typedef struct _MousepadDocumentClass   MousepadDocumentClass;
+typedef struct _MousepadDocument        MousepadDocument;
 
 #define MOUSEPAD_SCROLL_MARGIN 0.02
 
@@ -34,100 +38,66 @@ typedef struct _MousepadDocument      MousepadDocument;
 #define MOUSEPAD_IS_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_DOCUMENT))
 #define MOUSEPAD_DOCUMENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_DOCUMENT, MousepadDocumentClass))
 
-GType           mousepad_document_get_type                 (void) G_GNUC_CONST;
+struct _MousepadDocument
+{
+  GtkScrolledWindow        __parent__;
 
-GtkWidget      *mousepad_document_new                      (void);
+  /* private structure */
+  MousepadDocumentPrivate *priv;
 
-gboolean        mousepad_document_reload                   (MousepadDocument    *document,
-                                                            GError             **error);
+  /* file */
+  MousepadFile            *file;
 
-gboolean        mousepad_document_save_file                (MousepadDocument    *document,
-                                                            const gchar         *filename,
-                                                            GError             **error);
+  /* undo manager */
+  MousepadUndo            *undo;
 
-void            mousepad_document_set_filename             (MousepadDocument    *document,
-                                                            const gchar         *filename);
+  /* text buffer */
+  GtkTextBuffer           *buffer;
 
-void            mousepad_document_set_font                 (MousepadDocument    *document,
-                                                            const gchar         *font_name);
+  /* text view */
+  MousepadView            *textview;
 
-void            mousepad_document_set_auto_indent          (MousepadDocument    *document,
-                                                            gboolean             auto_indent);
+  /* the highlight tag */
+  GtkTextTag              *tag;
+};
 
-void            mousepad_document_set_line_numbers         (MousepadDocument    *document,
-                                                            gboolean             line_numbers);
+GType             mousepad_document_get_type                 (void) G_GNUC_CONST;
 
-void            mousepad_document_set_overwrite            (MousepadDocument    *document,
-                                                            gboolean             overwrite);
+MousepadDocument *mousepad_document_new                      (void);
 
-void            mousepad_document_set_word_wrap            (MousepadDocument    *document,
-                                                            gboolean             word_wrap);
+void              mousepad_document_set_font                 (MousepadDocument      *document,
+                                                              const gchar           *font_name);
 
-gboolean        mousepad_document_open_file                (MousepadDocument    *document,
-                                                            const gchar         *filename,
-                                                            GError             **error);
+void              mousepad_document_set_overwrite            (MousepadDocument      *document,
+                                                              gboolean               overwrite);
 
-gboolean        mousepad_document_find                     (MousepadDocument    *document,
-                                                            const gchar         *string,
-                                                            MousepadSearchFlags  flags);
+void              mousepad_document_set_word_wrap            (MousepadDocument      *document,
+                                                              gboolean               word_wrap);
 
-void            mousepad_document_replace                  (MousepadDocument    *document);
+void              mousepad_document_focus_textview           (MousepadDocument      *document);
 
-void            mousepad_document_highlight_all            (MousepadDocument    *document,
-                                                            const gchar         *string,
-                                                            MousepadSearchFlags  flags);
+void              mousepad_document_jump_to_line             (MousepadDocument      *document,
+                                                              gint                   line_number);
 
-void            mousepad_document_cut_selection            (MousepadDocument    *document);;
+void              mousepad_document_send_statusbar_signals   (MousepadDocument      *document);
 
-void            mousepad_document_copy_selection           (MousepadDocument    *document);
+void              mousepad_document_line_numbers             (MousepadDocument      *document,
+                                                              gint                  *current_line,
+                                                              gint                  *last_line);
 
-void            mousepad_document_paste_clipboard          (MousepadDocument    *document);
+gboolean          mousepad_document_get_readonly             (MousepadDocument      *document);
 
-void            mousepad_document_paste_column_clipboard   (MousepadDocument    *document);
+GtkWidget        *mousepad_document_get_tab_label            (MousepadDocument      *document);
 
-void            mousepad_document_delete_selection         (MousepadDocument    *document);
+const gchar      *mousepad_document_get_basename             (MousepadDocument      *document);
 
-void            mousepad_document_select_all               (MousepadDocument    *document);
+const gchar      *mousepad_document_get_filename             (MousepadDocument      *document);
 
-void            mousepad_document_focus_textview           (MousepadDocument    *document);
+gboolean          mousepad_document_get_word_wrap            (MousepadDocument      *document);
 
-void            mousepad_document_jump_to_line             (MousepadDocument    *document,
-                                                            gint                 line_number);
-
-void            mousepad_document_send_statusbar_signals   (MousepadDocument    *document);
-
-void            mousepad_document_line_numbers             (MousepadDocument    *document,
-                                                            gint                *current_line,
-                                                            gint                *last_line);
-
-gboolean        mousepad_document_get_externally_modified  (MousepadDocument    *document);
-
-gboolean        mousepad_document_get_can_undo             (MousepadDocument    *document);
-
-gboolean        mousepad_document_get_can_redo             (MousepadDocument    *document);
-
-const gchar    *mousepad_document_get_filename             (MousepadDocument    *document);
-
-gboolean        mousepad_document_get_has_selection        (MousepadDocument    *document);
-
-gboolean        mousepad_document_get_modified             (MousepadDocument    *document);
-
-gboolean        mousepad_document_get_readonly             (MousepadDocument    *document);
-
-GtkWidget      *mousepad_document_get_tab_label            (MousepadDocument    *document);
-
-const gchar    *mousepad_document_get_title                (MousepadDocument    *document,
-                                                            gboolean             show_full_path);
-
-gboolean        mousepad_document_get_word_wrap            (MousepadDocument    *document);
-
-gboolean        mousepad_document_get_line_numbers         (MousepadDocument    *document);
-
-gboolean        mousepad_document_get_auto_indent          (MousepadDocument    *document);
-
-void            mousepad_document_undo                     (MousepadDocument    *document);
-
-void            mousepad_document_redo                     (MousepadDocument    *document);
+void              mousepad_document_get_font_information     (MousepadDocument      *document,
+                                                              PangoFontDescription **font_desc,
+                                                              gint                  *font_height);
 
 G_END_DECLS
 
diff --git a/mousepad/mousepad-encoding-dialog.c b/mousepad/mousepad-encoding-dialog.c
new file mode 100644
index 0000000..02fb332
--- /dev/null
+++ b/mousepad/mousepad-encoding-dialog.c
@@ -0,0 +1,577 @@
+/* $Id$ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-encoding-dialog.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-util.h>
+
+
+
+static void     mousepad_encoding_dialog_class_init             (MousepadEncodingDialogClass *klass);
+static void     mousepad_encoding_dialog_init                   (MousepadEncodingDialog      *dialog);
+static void     mousepad_encoding_dialog_finalize               (GObject                     *object);
+static gboolean mousepad_encoding_dialog_test_encodings_idle    (gpointer                     user_data);
+static void     mousepad_encoding_dialog_test_encodings_destroy (gpointer                     user_data);
+static void     mousepad_encoding_dialog_test_encodings         (MousepadEncodingDialog      *dialog);
+static void     mousepad_encoding_dialog_read_file              (MousepadEncodingDialog      *dialog,
+                                                                 const gchar                 *encoding);
+static void     mousepad_encoding_dialog_button_toggled         (GtkWidget                   *button,
+                                                                 MousepadEncodingDialog      *dialog);
+static void     mousepad_encoding_dialog_combo_changed          (GtkComboBox                 *combo,
+                                                                 MousepadEncodingDialog      *dialog);
+
+
+
+enum
+{
+  COLUMN_LABEL,
+  COLUMN_ID,
+  COLUMN_UNPRINTABLE,
+  N_COLUMNS
+};
+
+struct _MousepadEncodingDialogClass
+{
+  GtkDialogClass __parent__;
+};
+
+struct _MousepadEncodingDialog
+{
+  GtkDialog __parent__;
+
+  /* the file */
+  MousepadDocument *document;
+
+  /* encoding test idle id */
+  guint          encoding_id;
+
+  /* encoding test position */
+  gint           encoding_n;
+
+  /* ok button */
+  GtkWidget     *button_ok;
+
+  /* error label and box */
+  GtkWidget     *error_box;
+  GtkWidget     *error_label;
+
+  /* progressbar */
+  GtkWidget     *progress;
+
+  /* the three radio button */
+  GtkWidget     *radio_utf8;
+  GtkWidget     *radio_system;
+  GtkWidget     *radio_other;
+
+  /* other encodings combo box */
+  GtkListStore  *store;
+  GtkWidget     *combo;
+};
+
+
+
+static const gchar *encodings[] =
+{
+"ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5", "ISO-8859-6",
+"ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-8859-10", "ISO-8859-11", "ISO-8859-13",
+"ISO-8859-14", "ISO-8859-15", "ISO-8859-16",
+
+"ARMSCII-8",
+"ASCII",
+"BIG5",
+"BIG5-HKSCS",
+"BIG5-HKSCS:1999",
+"BIG5-HKSCS:2001",
+"C99", "IBM850", "CP862", "CP866", "CP874", "CP932", "CP936", "CP949", "CP950",
+"CP1133", "CP1250", "CP1251", "CP1252", "CP1253", "CP1254", "CP1255", "CP1256", "CP1257", "CP1258",
+"EUC-CN",
+"EUC-JP",
+"EUC-KR",
+"EUC-TW",
+"GB18030",
+"GBK",
+"Georgian-Academy",
+"Georgian-PS",
+"HP-ROMAN8",
+"HZ",
+"ISO-2022-CN",
+"ISO-2022-CN-EXT",
+"ISO-2022-JP",
+"ISO-2022-JP-1",
+"ISO-2022-JP-2",
+"ISO-2022-KR",
+
+
+
+"JAVA",
+"JOHAB",
+"KOI8-R",
+"KOI8-RU",
+"KOI8-T",
+"KOI8-U",
+"MacArabic",
+"MacCentralEurope",
+"MacCroatian",
+"MacGreek",
+"MacHebrew",
+"MacIceland",
+"Macintosh",
+"MacMacRoman",
+"MacRomania",
+"MacThai",
+"MacTurkish",
+"MacUkraine",
+"MacCyrillic",
+"MuleLao-1",
+"NEXTSTEP",
+"PT154",
+"SHIFT_JIS",
+"TCVN",
+"TIS-620,",
+"UCS-2",
+"UCS-2-INTERNAL",
+"UCS-2BE",
+"UCS-2LE",
+"UCS-4",
+"UCS-4-INTERNAL",
+"UCS-4BE",
+"UCS-4LE",
+"UTF-16",
+"UTF-16BE",
+"UTF-16LE",
+"UTF-32",
+"UTF-32BE",
+"UTF-32LE",
+"UTF-7",
+"UTF-8",
+"VISCII"
+};
+
+
+
+static GObjectClass *mousepad_encoding_dialog_parent_class;
+
+
+
+GType
+mousepad_encoding_dialog_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+
+  if (G_UNLIKELY (type == G_TYPE_INVALID))
+    {
+      type = g_type_register_static_simple (GTK_TYPE_DIALOG,
+                                            I_("MousepadEncodingDialog"),
+                                            sizeof (MousepadEncodingDialogClass),
+                                            (GClassInitFunc) mousepad_encoding_dialog_class_init,
+                                            sizeof (MousepadEncodingDialog),
+                                            (GInstanceInitFunc) mousepad_encoding_dialog_init,
+                                            0);
+    }
+
+  return type;
+}
+
+
+
+static void
+mousepad_encoding_dialog_class_init (MousepadEncodingDialogClass *klass)
+{
+  GObjectClass   *gobject_class;
+
+  mousepad_encoding_dialog_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = mousepad_encoding_dialog_finalize;
+}
+
+
+
+static void
+mousepad_encoding_dialog_init (MousepadEncodingDialog *dialog)
+{
+  const gchar     *system_charset;
+  gchar           *system_label;
+  GtkWidget       *vbox, *hbox, *icon;
+  GtkCellRenderer *cell;
+
+  /* set some dialog properties */
+  gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+  gtk_window_set_default_size (GTK_WINDOW (dialog), 550, 350);
+
+  /* add buttons */
+  gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+  dialog->button_ok = gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+
+  /* create the header */
+  mousepad_util_dialog_header (GTK_DIALOG (dialog), _("The document was not UTF-8 valid"),
+                               _("Please select an encoding below."), GTK_STOCK_FILE);
+
+  /* dialog vbox */
+  vbox = gtk_vbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
+  gtk_widget_show (vbox);
+
+  hbox = gtk_hbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  /* encoding radio buttons */
+  dialog->radio_utf8 = gtk_radio_button_new_with_label (NULL, _("Default (UTF-8)"));
+  g_signal_connect (G_OBJECT (dialog->radio_utf8), "toggled", G_CALLBACK (mousepad_encoding_dialog_button_toggled), dialog);
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_utf8, FALSE, FALSE, 0);
+  gtk_widget_show (dialog->radio_utf8);
+
+  g_get_charset (&system_charset);
+  system_label = g_strdup_printf ("%s (%s)", _("System"), system_charset);
+  dialog->radio_system = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (dialog->radio_utf8), system_label);
+  g_signal_connect (G_OBJECT (dialog->radio_system), "toggled", G_CALLBACK (mousepad_encoding_dialog_button_toggled), dialog);
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_system, FALSE, FALSE, 0);
+  gtk_widget_show (dialog->radio_system);
+  g_free (system_label);
+
+  dialog->radio_other = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (dialog->radio_system), _("Other:"));
+  g_signal_connect (G_OBJECT (dialog->radio_other), "toggled", G_CALLBACK (mousepad_encoding_dialog_button_toggled), dialog);
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_other, FALSE, FALSE, 0);
+
+  /* create store */
+  dialog->store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
+
+  /* combobox with other charsets */
+  dialog->combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (dialog->store));
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->combo, TRUE, TRUE, 0);
+  g_signal_connect (G_OBJECT (dialog->combo), "changed", G_CALLBACK (mousepad_encoding_dialog_combo_changed), dialog);
+
+  /* text renderer for 1st column */
+  cell = gtk_cell_renderer_text_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (dialog->combo), cell, TRUE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (dialog->combo), cell, "text", COLUMN_LABEL, NULL);
+
+  /* progress bar */
+  dialog->progress = gtk_progress_bar_new ();
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->progress, TRUE, TRUE, 0);
+  gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress), "Testing encodings...");
+  gtk_widget_show (dialog->progress);
+
+  /* error box */
+  dialog->error_box = gtk_hbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (vbox), dialog->error_box, FALSE, FALSE, 0);
+
+  /* error icon */
+  icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_BUTTON);
+  gtk_box_pack_start (GTK_BOX (dialog->error_box), icon, FALSE, FALSE, 0);
+  gtk_widget_show (icon);
+
+  /* error label */
+  dialog->error_label = gtk_label_new (NULL);
+  gtk_box_pack_start (GTK_BOX (dialog->error_box), dialog->error_label, FALSE, FALSE, 0);
+  gtk_label_set_use_markup (GTK_LABEL (dialog->error_label), TRUE);
+  gtk_widget_show (dialog->error_label);
+
+  /* create text view */
+  dialog->document = mousepad_document_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (dialog->document), TRUE, TRUE, 0);
+  gtk_text_view_set_editable (GTK_TEXT_VIEW (dialog->document->textview), FALSE);
+  gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (dialog->document->textview), FALSE);
+  mousepad_view_set_line_numbers (dialog->document->textview, FALSE);
+  mousepad_document_set_word_wrap (dialog->document, FALSE);
+  gtk_widget_show (GTK_WIDGET (dialog->document));
+
+  /* lock undo manager forever */
+  mousepad_undo_lock (dialog->document->undo);
+}
+
+
+
+static void
+mousepad_encoding_dialog_finalize (GObject *object)
+{
+  MousepadEncodingDialog *dialog = MOUSEPAD_ENCODING_DIALOG (object);
+
+  /* stop running timeout */
+  if (G_UNLIKELY (dialog->encoding_id))
+    g_source_remove (dialog->encoding_id);
+
+  /* clear and release store */
+  gtk_list_store_clear (dialog->store);
+  g_object_unref (G_OBJECT (dialog->store));
+
+  (*G_OBJECT_CLASS (mousepad_encoding_dialog_parent_class)->finalize) (object);
+}
+
+
+
+static gboolean
+mousepad_encoding_dialog_test_encodings_idle (gpointer user_data)
+{
+  MousepadEncodingDialog *dialog = MOUSEPAD_ENCODING_DIALOG (user_data);
+  gdouble                 fraction;
+  gint                    unprintable, value;
+  const gchar            *encoding;
+  GtkTreeIter             iter, needle;
+
+  GDK_THREADS_ENTER ();
+
+  /* calculate the status */
+  fraction = (dialog->encoding_n + 1.00) / G_N_ELEMENTS (encodings);
+
+  /* set progress bar */
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress), fraction);
+
+  /* get the encoding */
+  encoding = encodings[dialog->encoding_n];
+
+  /* set an encoding */
+  unprintable = mousepad_file_test_encoding (dialog->document->file, encoding, NULL);
+
+  /* add the encoding to the combo box is the test succeed */
+  if (unprintable != -1)
+    {
+      /* get the first model iter, if there is one */
+      if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dialog->store), &needle))
+        {
+          while (TRUE)
+            {
+              /* get the column value */
+              gtk_tree_model_get (GTK_TREE_MODEL (dialog->store), &needle, COLUMN_UNPRINTABLE, &value, -1);
+
+              /* insert before the item with a higher number of unprintable characters */
+              if (value > unprintable)
+                {
+                  gtk_list_store_insert_before (dialog->store, &iter, &needle);
+
+                  break;
+                }
+
+              /* leave when we reached the end of the tree */
+              if (gtk_tree_model_iter_next (GTK_TREE_MODEL (dialog->store), &needle) == FALSE)
+                goto append_to_list;
+            }
+        }
+      else
+        {
+          append_to_list:
+
+          /* append to the list */
+          gtk_list_store_append (dialog->store, &iter);
+        }
+
+      /* set the column data */
+      gtk_list_store_set (dialog->store, &iter,
+                          COLUMN_LABEL, encoding,
+                          COLUMN_ID, dialog->encoding_n,
+                          COLUMN_UNPRINTABLE, unprintable,
+                          -1);
+    }
+
+  /* advance offset */
+  dialog->encoding_n++;
+
+  /* show the widgets when we're done */
+  if (dialog->encoding_n == G_N_ELEMENTS (encodings))
+    {
+      /* select the first item */
+      gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->combo), 0);
+
+      /* hide progress bar */
+      gtk_widget_hide (dialog->progress);
+
+      /* show the radio button and combo box */
+      gtk_widget_show (dialog->radio_other);
+      gtk_widget_show (dialog->combo);
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  return (dialog->encoding_n < G_N_ELEMENTS (encodings));
+}
+
+
+
+static void
+mousepad_encoding_dialog_test_encodings_destroy (gpointer user_data)
+{
+  MOUSEPAD_ENCODING_DIALOG (user_data)->encoding_id = 0;
+}
+
+
+
+static void
+mousepad_encoding_dialog_test_encodings (MousepadEncodingDialog *dialog)
+{
+  if (G_LIKELY (dialog->encoding_id == 0))
+    {
+      /* reset counter */
+      dialog->encoding_n = 0;
+
+      /* start new idle function */
+      dialog->encoding_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, mousepad_encoding_dialog_test_encodings_idle,
+                                             dialog, mousepad_encoding_dialog_test_encodings_destroy);
+    }
+}
+
+
+
+static void
+mousepad_encoding_dialog_read_file (MousepadEncodingDialog *dialog,
+                                    const gchar            *encoding)
+{
+  GtkTextIter  start, end;
+  GError      *error = NULL;
+  gchar       *message;
+  gboolean     succeed;
+
+  /* clear buffer */
+  gtk_text_buffer_get_bounds (dialog->document->buffer, &start, &end);
+  gtk_text_buffer_delete (dialog->document->buffer, &start, &end);
+
+  /* set encoding */
+  mousepad_file_set_encoding (dialog->document->file, encoding);
+
+  /* try to open the file */
+  succeed = mousepad_file_open (dialog->document->file, &error);
+
+  /* set sensitivity of the ok button */
+  gtk_widget_set_sensitive (dialog->button_ok, succeed);
+
+  if (succeed)
+    {
+      /* no error, hide the box */
+      gtk_widget_hide (dialog->error_box);
+    }
+  else
+    {
+      /* format message */
+      message = g_strdup_printf ("<b>%s.</b>", error->message);
+
+      /* set the error label */
+      gtk_label_set_markup (GTK_LABEL (dialog->error_label), message);
+
+      /* cleanup */
+      g_free (message);
+
+      /* show the error box */
+      gtk_widget_show (dialog->error_box);
+
+      /* clear the error */
+      g_error_free (error);
+    }
+}
+
+
+
+static void
+mousepad_encoding_dialog_button_toggled (GtkWidget              *button,
+                                         MousepadEncodingDialog *dialog)
+{
+  const gchar *system_charset;
+
+  /* ignore inactive buttons */
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+    {
+      /* set sensitivity of the other combobox */
+      gtk_widget_set_sensitive (dialog->combo, (button == dialog->radio_other));
+
+      if (button == dialog->radio_utf8)
+        {
+          /* open the file */
+          mousepad_encoding_dialog_read_file (dialog, NULL);
+        }
+      else if (button == dialog->radio_system)
+        {
+          /* get the system charset */
+          g_get_charset (&system_charset);
+
+          /* open the file */
+          mousepad_encoding_dialog_read_file (dialog, system_charset);
+        }
+      else
+        {
+          /* poke function */
+          mousepad_encoding_dialog_combo_changed (GTK_COMBO_BOX (dialog->combo), dialog);
+        }
+    }
+}
+
+
+
+static void
+mousepad_encoding_dialog_combo_changed (GtkComboBox            *combo,
+                                        MousepadEncodingDialog *dialog)
+{
+  GtkTreeIter iter;
+  gint        id;
+
+  /* get the selected item */
+  if (GTK_WIDGET_SENSITIVE (combo) && gtk_combo_box_get_active_iter (combo, &iter))
+    {
+      /* get the id */
+      gtk_tree_model_get (GTK_TREE_MODEL (dialog->store), &iter, COLUMN_ID, &id, -1);
+
+      /* open the file with other encoding */
+      mousepad_encoding_dialog_read_file (dialog, encodings[id]);
+    }
+}
+
+
+
+GtkWidget *
+mousepad_encoding_dialog_new (GtkWindow    *parent,
+                              MousepadFile *file)
+{
+  MousepadEncodingDialog *dialog;
+
+  _mousepad_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+
+  /* create the dialog */
+  dialog = g_object_new (MOUSEPAD_TYPE_ENCODING_DIALOG, NULL);
+
+  /* set parent window */
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+  /* set the filename */
+  mousepad_file_set_filename (dialog->document->file, mousepad_file_get_filename (file));
+
+  /* start with the system encoding */
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radio_system), TRUE);
+
+  /* queue idle function */
+  mousepad_encoding_dialog_test_encodings (dialog);
+
+  return GTK_WIDGET (dialog);
+}
+
+
+
+const gchar *
+mousepad_encoding_dialog_get_encoding (MousepadEncodingDialog *dialog)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_ENCODING_DIALOG (dialog), NULL);
+
+  return mousepad_file_get_encoding (dialog->document->file);
+}
diff --git a/mousepad/mousepad-encoding-dialog.h b/mousepad/mousepad-encoding-dialog.h
new file mode 100644
index 0000000..a992491
--- /dev/null
+++ b/mousepad/mousepad-encoding-dialog.h
@@ -0,0 +1,42 @@
+/* $Id$ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __MOUSEPAD_ENCODING_DIALOG_H__
+#define __MOUSEPAD_ENCODING_DIALOG_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_ENCODING_DIALOG            (mousepad_encoding_dialog_get_type ())
+#define MOUSEPAD_ENCODING_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_ENCODING_DIALOG, MousepadEncodingDialog))
+#define MOUSEPAD_ENCODING_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_ENCODING_DIALOG, MousepadEncodingDialogClass))
+#define MOUSEPAD_IS_ENCODING_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_ENCODING_DIALOG))
+#define MOUSEPAD_IS_ENCODING_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_ENCODING_DIALOG))
+#define MOUSEPAD_ENCODING_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_ENCODING_DIALOG, MousepadEncodingDialogClass))
+
+typedef struct _MousepadEncodingDialogClass MousepadEncodingDialogClass;
+typedef struct _MousepadEncodingDialog      MousepadEncodingDialog;
+
+GType        mousepad_encoding_dialog_get_type     (void) G_GNUC_CONST;
+
+GtkWidget   *mousepad_encoding_dialog_new          (GtkWindow              *parent,
+                                                    MousepadFile           *file);
+
+const gchar *mousepad_encoding_dialog_get_encoding (MousepadEncodingDialog *dialog);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_ENCODING_DIALOG_H__ */
diff --git a/mousepad/mousepad-file.c b/mousepad/mousepad-file.c
index aecb76d..1388985 100644
--- a/mousepad/mousepad-file.c
+++ b/mousepad/mousepad-file.c
@@ -19,375 +19,627 @@
 #include <config.h>
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef HAVE_MEMORY_H
-#include <memory.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
 #endif
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-#ifdef HAVE_TIME_H
-#include <time.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
 
 #include <mousepad/mousepad-private.h>
 #include <mousepad/mousepad-file.h>
 
 
-/**
- * mousepad_file_get_externally_modified:
- * @filename : The filename we're going to check.
- * @mtime    : The last modification time of the document.
- *
- * Returns whether a file has been modified after @mtime. If
- * no file was found it returns %FALSE.
- *
- * Return value: %TRUE is the file exists and has been modified.
- **/
-gboolean
-mousepad_file_get_externally_modified (const gchar *filename,
-                                       gint         mtime)
+#define MAX_ENCODING_CHECK_CHARS       (10000)
+#define MOUSEPAD_GET_LINE_END_CHARS(a) ((a) == MOUSEPAD_LINE_END_WIN32 ? "\r\n\0" : ((a) == MOUSEPAD_LINE_END_MAC ? "\r\0" : "\n\0"))
+
+
+enum
 {
-  gint         fd;
-  struct stat  statb;
-  gboolean     modified = FALSE;
+  EXTERNALLY_MODIFIED,
+  FILENAME_CHANGED,
+  LAST_SIGNAL
+};
+
+struct _MousepadFileClass
+{
+  GObjectClass __parent__;
+};
+
+struct _MousepadFile
+{
+  GObject             __parent__;
+
+  /* the text buffer this file belongs to */
+  GtkTextBuffer      *buffer;
+
+  /* filename */
+  gchar              *filename;
+
+  /* encoding of the file */
+  gchar              *encoding;
 
-  _mousepad_return_val_if_fail (filename != NULL, TRUE);
+  /* line ending of the file */
+  MousepadLineEnding  line_ending;
 
-  /* open the file for reading */
-  fd = open (filename, O_RDONLY);
-  if (G_UNLIKELY (fd < 0))
+  /* our last modification time */
+  gint                mtime;
+
+  /* if file is read-only */
+  guint               readonly : 1;
+};
+
+
+
+static void  mousepad_file_class_init       (MousepadFileClass  *klass);
+static void  mousepad_file_init             (MousepadFile       *file);
+static void  mousepad_file_finalize         (GObject            *object);
+
+
+
+static GObjectClass *mousepad_file_parent_class;
+static guint         file_signals[LAST_SIGNAL];
+
+
+
+GType
+mousepad_file_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+
+  if (G_UNLIKELY (type == G_TYPE_INVALID))
     {
-      /* files probably doesn't exists, so we can safely write */
-      return FALSE;
+      type = g_type_register_static_simple (G_TYPE_OBJECT,
+                                            I_("MousepadFile"),
+                                            sizeof (MousepadFileClass),
+                                            (GClassInitFunc) mousepad_file_class_init,
+                                            sizeof (MousepadFile),
+                                            (GInstanceInitFunc) mousepad_file_init,
+                                            0);
     }
 
-  /* check if the file has been modified */
-  if (G_LIKELY (fstat (fd, &statb) == 0))
-    modified = (mtime > 0 && statb.st_mtime != mtime);
+  return type;
+}
 
-  /* close the file */
-  close (fd);
 
-  return modified;
+
+static void
+mousepad_file_class_init (MousepadFileClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  mousepad_file_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = mousepad_file_finalize;
+
+  file_signals[EXTERNALLY_MODIFIED] =
+    g_signal_new (I_("externally-modified"),
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_NO_HOOKS,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__BOOLEAN,
+                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+  file_signals[FILENAME_CHANGED] =
+    g_signal_new (I_("filename-changed"),
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_NO_HOOKS,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__STRING,
+                  G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+
+
+static void
+mousepad_file_init (MousepadFile *file)
+{
+  /* initialize */
+  file->filename    = NULL;
+  file->encoding    = NULL;
+  file->line_ending = MOUSEPAD_LINE_END_NONE;
+  file->readonly    = FALSE;
+  file->mtime       = 0;
+}
+
+
+
+static void
+mousepad_file_finalize (GObject *object)
+{
+  MousepadFile *file = MOUSEPAD_FILE (object);
+
+  /* cleanup */
+  g_free (file->filename);
+  g_free (file->encoding);
+
+  /* release the reference from the buffer */
+  g_object_unref (G_OBJECT (file->buffer));
+
+  (*G_OBJECT_CLASS (mousepad_file_parent_class)->finalize) (object);
+}
+
+
+
+MousepadFile *
+mousepad_file_new (GtkTextBuffer *buffer)
+{
+  MousepadFile *file;
+
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+
+  file = g_object_new (MOUSEPAD_TYPE_FILE, NULL);
+
+  /* set the buffer */
+  file->buffer = g_object_ref (G_OBJECT (buffer));
+
+  return file;
+}
+
+
+
+void
+mousepad_file_set_filename (MousepadFile *file,
+                            const gchar  *filename)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+  /* free the old filename */
+  g_free (file->filename);
+
+  /* set the filename */
+  file->filename = g_strdup (filename);
+
+  /* send a signal that the name has been changed */
+  g_signal_emit (G_OBJECT (file), file_signals[FILENAME_CHANGED], 0, file->filename);
+}
+
+
+
+const gchar *
+mousepad_file_get_filename (MousepadFile *file)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+
+  return file->filename;
+}
+
+
+
+gchar *
+mousepad_file_get_uri (MousepadFile *file)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+
+  return g_filename_to_uri (file->filename, NULL, NULL);
+}
+
+
+
+void
+mousepad_file_set_encoding (MousepadFile *file,
+                            const gchar  *encoding)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+  /* cleanup */
+  g_free (file->encoding);
+
+  /* set new encoding */
+  file->encoding = g_strdup (encoding);
+}
+
+
+
+const gchar *
+mousepad_file_get_encoding (MousepadFile *file)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+
+  return file->encoding ? file->encoding : "UTF-8";
+}
+
+
+
+void
+mousepad_file_set_line_ending (MousepadFile       *file,
+                               MousepadLineEnding  line_ending)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+  file->line_ending = line_ending;
+}
+
+
+
+MousepadLineEnding
+mousepad_file_get_line_ending (MousepadFile *file)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), MOUSEPAD_LINE_END_NONE);
+
+  return file->line_ending;
 }
 
 
 
-/**
- * mousepad_file_save_data:
- * @filename  : The filename of the document.
- * @data      : The content of the editor. This string should
- *              already been converted to the correct encoding.
- * @bytes     : The length of @data.
- * @new_mtime : Return location for the file's modification
- *              time after a succesfull write.
- * @error     : Return location for errors or %NULL.
- *
- * Try to save the @data to @filename. This function does not
- * check if the document has been modified externally.
- *
- * Return value: %TRUE on success, %FALSE if @error is set.
- **/
 gboolean
-mousepad_file_save_data (const gchar  *filename,
-                         const gchar  *data,
-                         gsize         bytes,
-                         gint         *new_mtime,
-                         GError      **error)
+mousepad_file_open (MousepadFile  *file,
+                    GError       **error)
 {
-  gint         fd;
-  struct stat  statb;
+  GIOChannel  *channel;
+  GIOStatus    status;
+  gsize        length, terminator_pos;
   gboolean     succeed = FALSE;
-  gint         n, m, l;
+  gchar       *string;
+  const gchar *line_term;
+  GtkTextIter  start, end;
+  struct stat  statb;
 
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (file->buffer), FALSE);
   _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  _mousepad_return_val_if_fail (filename != NULL, FALSE);
-  _mousepad_return_val_if_fail (data != NULL, FALSE);
+  _mousepad_return_val_if_fail (file->filename != NULL, FALSE);
 
-  /* try to create the file (this failes if it already exists) */
-  fd = open (filename, O_CREAT | O_EXCL | O_WRONLY, 0666);
-  if (fd < 0)
-    {
-      if (G_LIKELY (errno == EEXIST))
-        {
-          /* try to open an existing file for reading */
-          fd = open (filename, O_RDWR);
-          if (G_UNLIKELY (fd < 0))
-            {
-              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_IO,
-                           _("Failed to open \"%s\" for writing"), filename);
-              return FALSE;
-             }
-        }
-      else
-        {
-          /* failed to create a file */
-          g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                       _("Failed to create new file \"%s\""), filename);
-          return FALSE;
-        }
-    }
+  /* check if the file exists */
+  if (g_file_test (file->filename, G_FILE_TEST_EXISTS) == FALSE)
+    return TRUE;
 
-  /* get the file information */
-  if (G_UNLIKELY (fstat (fd, &statb) != 0))
-    goto failed;
+  /* open the channel */
+  channel = g_io_channel_new_file (file->filename, "r", error);
 
-  /* an extra check if we are allowed to write */
-  if (G_UNLIKELY ((statb.st_mode & 00222) == 0 && access (filename, W_OK) != 0))
+  if (G_LIKELY (channel))
     {
-      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                   _("You don't have permission to write to \"%s\""), filename);
-      goto failed;
-    }
+      /* freeze notifications */
+      g_object_freeze_notify (G_OBJECT (file->buffer));
 
-  /* make sure the document is empty and we're writing at the beginning of the file */
-  if (G_LIKELY (lseek (fd, 0, SEEK_SET) != -1))
-    if (G_UNLIKELY (ftruncate (fd, 0) != 0))
-      {
-        g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                     _("Failed to truncate \"%s\" before writing"), filename);
-        goto failed;
-      }
+      /* set the encoding of the channel */
+      if (file->encoding != NULL)
+        {
+          /* set the encoding */
+          status = g_io_channel_set_encoding (channel, file->encoding, error);
 
-  /* write the content to the file */
-  for (m = 0, n = bytes; m < n; )
-    {
-      /* write the data piece by piece */
-      l = write (fd, data + m, n - m);
+          if (G_UNLIKELY (status != G_IO_STATUS_NORMAL))
+            goto failed;
+        }
 
-      if (G_UNLIKELY (l < 0))
+      /* get the iter at the beginning of the document */
+      gtk_text_buffer_get_start_iter (file->buffer, &end);
+
+      /* read the content of the file */
+      while (TRUE)
         {
-          /* just try again on EAGAIN/EINTR */
-          if (G_LIKELY (errno != EAGAIN && errno != EINTR))
+          /* read the line */
+          status = g_io_channel_read_line (channel, &string, &length, &terminator_pos, error);
+
+          /* leave on problems */
+          if (G_UNLIKELY (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_EOF))
+            goto failed;
+
+          if (G_LIKELY (string))
             {
-              g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                           _("Failed to write data to \"%s\""), filename);
-              goto failed;
+              /* detect the line ending of the file */
+              if (length != terminator_pos
+                  && file->line_ending == MOUSEPAD_LINE_END_NONE)
+                {
+                  /* get the characters */
+                  line_term = string + terminator_pos;
+
+                  if (strcmp (line_term, "\n") == 0)
+                    file->line_ending = MOUSEPAD_LINE_END_UNIX;
+                  else if (strcmp (line_term, "\r") == 0)
+                    file->line_ending = MOUSEPAD_LINE_END_MAC;
+                  else if (strcmp (line_term, "\r\n") == 0)
+                    file->line_ending = MOUSEPAD_LINE_END_WIN32;
+                  else
+                    g_warning (_("Unknown line ending detected (%s)"), line_term);
+                }
+
+              /* make sure the string utf-8 valid */
+              if (G_UNLIKELY (g_utf8_validate (string, length, NULL) == FALSE))
+                {
+                  /* set an error */
+                  g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+                               _("Converted string is not UTF-8 valid"));
+
+                  /* free string */
+                  g_free (string);
+
+                  goto failed;
+                }
+
+              /* insert the string in the buffer */
+              gtk_text_buffer_insert (file->buffer, &end, string, length);
+
+              /* cleanup */
+              g_free (string);
             }
+
+          /* break when we've reached the end of the file */
+          if (G_UNLIKELY (status == G_IO_STATUS_EOF))
+            break;
         }
-      else
+
+      /* done */
+      succeed = TRUE;
+
+      /* get the start iter */
+      gtk_text_buffer_get_start_iter (file->buffer, &start);
+
+      /* set the cursor to the beginning of the document */
+      gtk_text_buffer_place_cursor (file->buffer, &start);
+
+      /* check if we're allowed to write to the file */
+      if (G_LIKELY (g_lstat (file->filename, &statb) == 0) && access (file->filename, W_OK) == 0)
+        file->readonly = !((statb.st_mode & 00222) != 0);
+
+      failed:
+
+      /* empty the buffer if we did not succeed */
+      if (G_UNLIKELY (succeed == FALSE))
         {
-          /* advance the offset */
-          m += l;
+          gtk_text_buffer_get_bounds (file->buffer, &start, &end);
+          gtk_text_buffer_delete (file->buffer, &start, &end);
         }
-    }
 
-  /* re-stat the mtime */
-  if (G_UNLIKELY (fstat (fd, &statb) == 0))
-    *new_mtime = statb.st_mtime;
+      /* this does not count as a modified buffer */
+      gtk_text_buffer_set_modified (file->buffer, FALSE);
+
+      /* thawn notifications */
+      g_object_thaw_notify (G_OBJECT (file->buffer));
 
-  /* mm.. it seems everything worked fine */
-  succeed = TRUE;
+      /* close the channel and flush the write buffer */
+      g_io_channel_shutdown (channel, TRUE, NULL);
 
-failed:
-  /* close the file */
-  close (fd);
+      /* release the channel */
+      g_io_channel_unref (channel);
+    }
 
   return succeed;
 }
 
 
 
-/**
- * mousepad_file_read_to_buffer:
- * @filename  : The filename of the destination file.
- * @buffer    : A #GtkTextBuffer where we can insert the
- *              content of @filename.
- * @new_mtime : Return location of the file's modification time
- *              after a succesfull read.
- * @readonly  : Return location if we're only allowed to write
- *              the file. %TRUE if the user is not allowed to write
- *              to the file.
- * @error     : Return location of errors or %NULL.
- *
- * This function reads the content of a file and inserts it directly
- * in a #GtkTextBuffer. We don't return a string to avoid string duplication.
- *
- * If the user has support for MMAP and the size of the file is below 8MB, we
- * use MMAP to speedup the file reading a bit.
- *
- * Return value: %TRUE on success, %FALSE if @error is set.
- **/
 gboolean
-mousepad_file_read_to_buffer (const gchar    *filename,
-                              GtkTextBuffer  *buffer,
-                              gint           *new_mtime,
-                              gboolean       *readonly,
-                              GError        **error)
+mousepad_file_save (MousepadFile  *file,
+                    GError       **error)
 {
-  GtkTextIter    start_iter;
-  struct stat    statb;
-  gchar         *content;
-  gint           fd;
-  gboolean       succeed = FALSE;
+  GIOChannel  *channel;
+  GIOStatus    status;
+  gboolean     succeed = FALSE;
+  gchar       *slice;
+  const gchar *line_term;
+  GtkTextIter  start, end;
+  struct stat  statb;
 
-  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (file->buffer), FALSE);
   _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  _mousepad_return_val_if_fail (filename != NULL, FALSE);
+  _mousepad_return_val_if_fail (file->filename != NULL, FALSE);
 
-  /* open the file for reading */
-  fd = open (filename, O_RDONLY);
-  if (G_UNLIKELY (fd < 0))
+  /* open the channel. the file is created or truncated */
+  channel = g_io_channel_new_file (file->filename, "w", error);
+
+  if (G_LIKELY (channel))
     {
-      /* the file does not exists, so probably the user ran 'mousepad <new-file-name>' from the command line */
-      if (G_LIKELY (errno == ENOENT))
+      /* set the encoding of the channel */
+      if (file->encoding != NULL)
         {
-          /* we can write the new file */
-          *readonly = FALSE;
+          /* set the encoding */
+          status = g_io_channel_set_encoding (channel, file->encoding, error);
 
-          return TRUE;
+          if (G_UNLIKELY (status != G_IO_STATUS_NORMAL))
+            goto failed;
         }
-      else
+
+      /* get the line ending characters we're going to use */
+      line_term = MOUSEPAD_GET_LINE_END_CHARS (file->line_ending);
+
+      /* get the start iter of the buffer */
+      gtk_text_buffer_get_start_iter (file->buffer, &start);
+
+      /* walk the buffer */
+      do
         {
-          g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                       _("Failed to open \"%s\" for reading"), filename);
-          return FALSE;
-        }
-    }
+          /* set the end iter */
+          end = start;
 
-  /* read the file information */
-  if (G_UNLIKELY (fstat (fd, &statb) < 0))
-    goto failed;
+          /* insert the text if this line is not empty */
+          if (gtk_text_iter_ends_line (&start) == FALSE)
+            {
+              /* move to the end of the line */
+              gtk_text_iter_forward_to_line_end (&end);
 
-  /* extra check if we're allowed to read the file */
-  if ((statb.st_mode & 00444) == 0 && access (filename, R_OK) != 0)
-    {
-      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                   _("You are not allowed to read \"%s\""), filename);
-      goto failed;
-    }
+              /* get the test slice */
+              slice = gtk_text_buffer_get_slice (file->buffer, &start, &end, TRUE);
 
-  /* check if we're allowed to write the file */
-  *readonly = !((statb.st_mode & 00222) != 0 && access (filename, W_OK) == 0);
+              /* write the file content */
+              status = g_io_channel_write_chars (channel, slice, -1, NULL, error);
 
-  /* get the start point in the buffer */
-  gtk_text_buffer_get_start_iter (buffer, &start_iter);
+              /* cleanup */
+              g_free (slice);
 
-#ifdef HAVE_MMAP
-  /* try mmap() for files not larger than 8MB */
-  content = (statb.st_size <= 8 * 1024 * 1024)
-            ? (gchar *) mmap (NULL, statb.st_size, PROT_READ, MAP_SHARED, fd, 0)
-            : MAP_FAILED;
+              /* leave when an error occured */
+              if (G_UNLIKELY (status != G_IO_STATUS_NORMAL))
+                goto failed;
+            }
 
-  if (G_LIKELY (content != (gchar *) MAP_FAILED))
-    {
-#ifdef HAVE_POSIX_MADVISE
-      /* tell the system that the data will be read sequentially */
-      posix_madvise (content, statb.st_size, POSIX_MADV_SEQUENTIAL);
-#endif
+          /* insert a new line if we haven't reached the end of the buffer */
+          if (gtk_text_iter_is_end (&end) == FALSE)
+            {
+              /* insert the new line */
+              status = g_io_channel_write_chars (channel, line_term, -1, NULL, error);
 
-      /* insert the file content */
-      gtk_text_buffer_insert (buffer, &start_iter, content, strlen (content));
+              /* leave when an error occured */
+              if (G_UNLIKELY (status != G_IO_STATUS_NORMAL))
+                goto failed;
+            }
+        }
+      while (gtk_text_iter_forward_line (&start));
 
-      /* unmap */
-      munmap (content, statb.st_size);
-    }
-  else
-#endif /* !HAVE_MMAP */
-    {
-      /* load the file content by using the glib function */
-      if (G_UNLIKELY (g_file_get_contents (filename, &content, NULL, error) == FALSE))
-        goto failed;
+      /* everything seems to be ok */
+      succeed = TRUE;
 
-      /* insert the file content */
-      gtk_text_buffer_insert (buffer, &start_iter, content, strlen (content));
+      /* set the new modification time */
+      if (G_LIKELY (g_lstat (file->filename, &statb) == 0))
+        file->mtime = statb.st_mtime;
 
-      /* cleanup */
-      g_free (content);
-    }
+      /* everything has been saved */
+      gtk_text_buffer_set_modified (file->buffer, FALSE);
+
+      /* we saved succesfully */
+      file->readonly = FALSE;
 
-  /* set the mtime of the document */
-  *new_mtime = statb.st_mtime;
+      failed:
 
-  /* everything went ok */
-  succeed = TRUE;
+      /* close the channel and flush the write buffer */
+      g_io_channel_shutdown (channel, TRUE, error);
 
-failed:
-  /* close the file */
-  close (fd);
+      /* release the channel */
+      g_io_channel_unref (channel);
+    }
 
   return succeed;
 }
 
 
 
-/**
- * mousepad_file_is_writable:
- * @filename : The filename to check.
- * @error    : Return location of errors or %NULL.
- *
- * Check if a user is allowed to write to a file. This function
- * is used in the save as dialog to warn the user if he or she
- * has permissions to write (before actually closing the
- * dialog).
- *
- * Return value: %TRUE is the user is allowed to write the @filename.
- **/
 gboolean
-mousepad_file_is_writable (const gchar  *filename,
-                           GError      **error)
+mousepad_file_reload (MousepadFile  *file,
+                      GError       **error)
 {
-  struct stat statb;
-  gint        fd;
-  gboolean    writable = FALSE;
+  GtkTextIter start, end;
+  gboolean    succeed = FALSE;
 
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (file->buffer), FALSE);
   _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  _mousepad_return_val_if_fail (filename != NULL, FALSE);
+  _mousepad_return_val_if_fail (file->filename != NULL, FALSE);
 
-  fd = open (filename, O_RDONLY);
-  if (G_UNLIKELY (fd < 0))
+  /* simple test if the file has not been removed */
+  if (G_UNLIKELY (g_file_test (file->filename, G_FILE_TEST_EXISTS) == FALSE))
     {
-      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                   _("Failed to open \"%s\" for reading"), filename);
+      /* set an error */
+      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
+                   _("The file \"%s\" you've tried to reload does not exist anymore"), file->filename);
+
       return FALSE;
     }
 
-  /* read the file information */
-  if (G_UNLIKELY (fstat (fd, &statb) < 0))
-    goto failed;
+  /* clear the buffer */
+  gtk_text_buffer_get_bounds (file->buffer, &start, &end);
+  gtk_text_buffer_delete (file->buffer, &start, &end);
 
-  /* get the file information */
-  if (G_UNLIKELY (fstat (fd, &statb) != 0))
-    goto failed;
+  /* reload the file */
+  succeed = mousepad_file_open (file, error);
+
+  return succeed;
+}
+
+
+
+gboolean
+mousepad_file_get_externally_modified (MousepadFile  *file,
+                                       GError       **error)
+{
+  struct stat statb;
+  gboolean    modified = FALSE;
 
-  /* an extra check if we are allowed to write */
-  if ((statb.st_mode & 00222) != 0 && access (filename, W_OK) == 0)
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+  _mousepad_return_val_if_fail (file->filename == NULL, FALSE);
+  _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (G_LIKELY (g_lstat (file->filename, &statb) == 0))
     {
-      writable = TRUE;
+      /* check if our modification time differs from the current one */
+      modified = (file->mtime > 0 && statb.st_mtime != file->mtime);
     }
   else
     {
-      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                   _("You don't have permission to write to \"%s\""), filename);
-      goto failed;
+      /* failed to stat the file */
+      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+                   _("Failed to read the status of \"%s\""), file->filename);
     }
 
+  return modified;
+}
 
-failed:
-  /* close the file */
-  close (fd);
 
-  return writable;
-}
 
+gint
+mousepad_file_test_encoding (MousepadFile  *file,
+                             const gchar   *encoding,
+                             GError       **error)
+{
+  GIOChannel  *channel;
+  GIOStatus    status;
+  gunichar     c;
+  gint         unprintable = -1;
+  gint         counter = 0;
+
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), -1);
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (file->buffer), -1);
+  _mousepad_return_val_if_fail (error == NULL || *error == NULL, -1);
+  _mousepad_return_val_if_fail (file->filename != NULL, -1);
+  _mousepad_return_val_if_fail (encoding != NULL, -1);
+
+  /* check if the file exists */
+  if (g_file_test (file->filename, G_FILE_TEST_EXISTS) == FALSE)
+    return -1;
+
+  /* open the channel */
+  channel = g_io_channel_new_file (file->filename, "r", error);
+
+  if (G_LIKELY (channel))
+    {
+      /* set the encoding of the channel */
+      status = g_io_channel_set_encoding (channel, encoding, error);
 
+      if (G_UNLIKELY (status != G_IO_STATUS_NORMAL))
+        goto failed;
+
+      /* set the counter */
+      unprintable = 0;
+
+      /* read the characters in the file and break on large files */
+      while (G_UNLIKELY (counter < MAX_ENCODING_CHECK_CHARS))
+        {
+          /* read a character */
+          status = g_io_channel_read_unichar (channel, &c, error);
+
+          /* leave on problems */
+          if (G_UNLIKELY (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_EOF))
+            {
+              /* reset counter */
+              unprintable = -1;
 
+              goto failed;
+            }
+
+          /* check if the character is unprintable, but not a soft hyphen */
+          if (G_UNLIKELY (g_unichar_isprint (c) == FALSE
+                          && g_unichar_break_type (c) != G_UNICODE_BREAK_AFTER))
+            unprintable++;
+
+          /* break when we've reached the end of the file */
+          if (G_UNLIKELY (status == G_IO_STATUS_EOF))
+            break;
+
+          /* increase counter */
+          counter++;
+        }
+
+      failed:
+
+      /* close the channel and flush the write buffer */
+      g_io_channel_shutdown (channel, TRUE, NULL);
+
+      /* release the channel */
+      g_io_channel_unref (channel);
+    }
+
+  return unprintable;
+}
diff --git a/mousepad/mousepad-file.h b/mousepad/mousepad-file.h
index c5120be..d76ac4a 100644
--- a/mousepad/mousepad-file.h
+++ b/mousepad/mousepad-file.h
@@ -20,23 +20,61 @@
 
 G_BEGIN_DECLS
 
-gboolean  mousepad_file_get_externally_modified  (const gchar    *filename,
-                                                  gint            mtime);
-
-gboolean  mousepad_file_save_data                (const gchar    *filename,
-                                                  const gchar    *data,
-                                                  gsize           bytes,
-                                                  gint           *new_mtime,
-                                                  GError        **error);
-
-gboolean  mousepad_file_read_to_buffer           (const gchar    *filename,
-                                                  GtkTextBuffer  *buffer,
-                                                  gint           *new_mtime,
-                                                  gboolean       *readonly,
-                                                  GError        **error);
-
-gboolean  mousepad_file_is_writable              (const gchar    *filename,
-                                                  GError        **error);
+typedef struct _MousepadFileClass  MousepadFileClass;
+typedef struct _MousepadFile       MousepadFile;
+typedef enum   _MousepadLineEnding MousepadLineEnding;
+
+#define MOUSEPAD_TYPE_FILE            (mousepad_file_get_type ())
+#define MOUSEPAD_FILE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_FILE, MousepadFile))
+#define MOUSEPAD_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_FILE, MousepadFileClass))
+#define MOUSEPAD_IS_FILE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_FILE))
+#define MOUSEPAD_IS_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_FILE))
+#define MOUSEPAD_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_FILE, MousepadFileClass))
+
+enum _MousepadLineEnding
+{
+  MOUSEPAD_LINE_END_NONE,
+  MOUSEPAD_LINE_END_UNIX,
+  MOUSEPAD_LINE_END_WIN32,
+  MOUSEPAD_LINE_END_MAC
+};
+
+GType               mousepad_file_get_type                 (void) G_GNUC_CONST;
+
+MousepadFile       *mousepad_file_new                      (GtkTextBuffer       *buffer);
+
+void                mousepad_file_set_filename             (MousepadFile        *file,
+                                                            const gchar         *filename);
+
+const gchar        *mousepad_file_get_filename             (MousepadFile        *file);
+
+gchar              *mousepad_file_get_uri                  (MousepadFile        *file);
+
+void                mousepad_file_set_encoding             (MousepadFile        *file,
+                                                            const gchar         *encoding);
+
+const gchar        *mousepad_file_get_encoding             (MousepadFile        *file);
+
+void                mousepad_file_set_line_ending          (MousepadFile        *file,
+                                                            MousepadLineEnding   line_ending);
+
+MousepadLineEnding  mousepad_file_get_line_ending          (MousepadFile        *file);
+
+gboolean            mousepad_file_open                     (MousepadFile        *file,
+                                                            GError             **error);
+
+gboolean            mousepad_file_save                     (MousepadFile        *file,
+                                                            GError             **error);
+
+gboolean            mousepad_file_reload                   (MousepadFile        *file,
+                                                            GError             **error);
+
+gboolean            mousepad_file_get_externally_modified  (MousepadFile        *file,
+                                                            GError             **error);
+
+gint                mousepad_file_test_encoding            (MousepadFile        *file,
+                                                            const gchar         *encoding,
+                                                            GError             **error);
 
 G_END_DECLS
 
diff --git a/mousepad/mousepad-marshal.list b/mousepad/mousepad-marshal.list
index 37e8a58..ba02bfc 100644
--- a/mousepad/mousepad-marshal.list
+++ b/mousepad/mousepad-marshal.list
@@ -1,2 +1,3 @@
 VOID:UINT,UINT
-BOOLEAN:STRING,FLAGS
+INT:FLAGS,STRING,STRING
+VOID:OBJECT,INT,INT
diff --git a/mousepad/mousepad-preferences.c b/mousepad/mousepad-preferences.c
index e8f4a7f..a372e19 100644
--- a/mousepad/mousepad-preferences.c
+++ b/mousepad/mousepad-preferences.c
@@ -45,20 +45,31 @@
 enum
 {
   PROP_0,
-  PROP_FONT_NAME,
-  PROP_LAST_AUTO_INDENT,
-  PROP_LAST_LINE_NUMBERS,
-  PROP_LAST_MATCH_CASE,
-  PROP_LAST_MATCH_WHOLE_WORD,
-  PROP_LAST_STATUSBAR_VISIBLE,
-  PROP_LAST_WINDOW_HEIGHT,
-  PROP_LAST_WINDOW_WIDTH,
-  PROP_LAST_WORD_WRAP,
-  PROP_LAST_WRAP_AROUND,
+
+  /* search preferences */
+  PROP_SEARCH_DIRECTION,
+  PROP_SEARCH_MATCH_CASE,
+  PROP_SEARCH_MATCH_WHOLE_WORD,
+  PROP_SEARCH_REPLACE_ALL_LOCATION,
+
+  /* textview preferences */
+  PROP_VIEW_AUTO_INDENT,
+  PROP_VIEW_FONT_NAME,
+  PROP_VIEW_LINE_NUMBERS,
+  PROP_VIEW_TAB_WIDTH,
+  PROP_VIEW_TABS_AS_SPACES,
+  PROP_VIEW_WORD_WRAP,
+
+  /* window preferences */
+  PROP_WINDOW_HEIGHT,
+  PROP_WINDOW_WIDTH,
+  PROP_WINDOW_STATUSBAR_VISIBLE,
+
+  /* hidden settings */
   PROP_MISC_ALWAYS_SHOW_TABS,
   PROP_MISC_CYCLE_TABS,
-  PROP_MISC_SHOW_FULL_PATH_IN_TITLE,
-  PROP_MISC_RECENT_MENU_LIMIT,
+  PROP_MISC_PATH_IN_TITLE,
+  PROP_MISC_RECENT_MENU_ITEMS,
   PROP_MISC_REMEMBER_GEOMETRY,
   N_PROPERTIES,
 };
@@ -78,8 +89,11 @@ static void     mousepad_preferences_set_property       (GObject
                                                          guint                     prop_id,
                                                          const GValue             *value,
                                                          GParamSpec               *pspec);
-static void     mousepad_preferences_queue_store        (MousepadPreferences      *preferences);
-static gboolean mousepad_preferences_load               (MousepadPreferences      *preferences);
+#ifndef NDEBUG
+static void     mousepad_preferences_check_option_name  (GParamSpec               *pspec);
+#endif
+static void     mousepad_preferences_load               (MousepadPreferences      *preferences);
+static void     mousepad_preferences_store              (MousepadPreferences      *preferences);
 static gboolean mousepad_preferences_store_idle         (gpointer                  user_data);
 static void     mousepad_preferences_store_idle_destroy (gpointer                  user_data);
 
@@ -92,7 +106,7 @@ struct _MousepadPreferences
 {
   GObject  __parent__;
 
-  /* all the properties stored in the Object */
+  /* all the properties stored in the object */
   GValue   values[N_PROPERTIES];
 
   /* idle save timer id */
@@ -172,211 +186,166 @@ mousepad_preferences_class_init (MousepadPreferencesClass *klass)
   gobject_class->set_property = mousepad_preferences_set_property;
 
   /* register transformation functions */
-  if (!g_value_type_transformable (G_TYPE_STRING, G_TYPE_BOOLEAN))
+  if (G_LIKELY (g_value_type_transformable (G_TYPE_STRING, G_TYPE_BOOLEAN) == FALSE))
     g_value_register_transform_func (G_TYPE_STRING, G_TYPE_BOOLEAN, transform_string_to_boolean);
-  if (!g_value_type_transformable (G_TYPE_STRING, G_TYPE_INT))
+  if (G_LIKELY (g_value_type_transformable (G_TYPE_STRING, G_TYPE_INT) == FALSE))
     g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, transform_string_to_int);
 
   /**
-   * MousepadPreferences:font-name
-   *
-   * The font name and size used in the text view. If this value is
-   * %NULL, the system font will be used.
+   * Search Preferences
    **/
   g_object_class_install_property (gobject_class,
-                                   PROP_FONT_NAME,
-                                   g_param_spec_string ("font-name",
-                                                        "font-name",
-                                                        "font-name",
-                                                        NULL,
-                                                        MOUSEPAD_PARAM_READWRITE));
+                                   PROP_SEARCH_DIRECTION,
+                                   g_param_spec_int ("search-direction",
+                                                     "SearchDirection",
+                                                     NULL,
+                                                     0, 2, 1,
+                                                     MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:last-auto-indent
-   *
-   * Whether we auto indent lines
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_AUTO_INDENT,
-                                   g_param_spec_boolean ("last-auto-indent",
-                                                         "last-auto-indent",
-                                                         "last-auto-indent",
+                                   PROP_SEARCH_MATCH_CASE,
+                                   g_param_spec_boolean ("search-match-case",
+                                                         "SearchMatchCase",
+                                                         NULL,
                                                          FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:last-line-numbers
-   *
-   * Whether line numbers are visible
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_LINE_NUMBERS,
-                                   g_param_spec_boolean ("last-line-numbers",
-                                                         "last-line-numbers",
-                                                         "last-line-numbers",
+                                   PROP_SEARCH_MATCH_WHOLE_WORD,
+                                   g_param_spec_boolean ("search-match-whole-word",
+                                                         "SearchMatchWholeWord",
+                                                         NULL,
                                                          FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
+  g_object_class_install_property (gobject_class,
+                                   PROP_SEARCH_REPLACE_ALL_LOCATION,
+                                   g_param_spec_int ("search-replace-all-location",
+                                                     "SearchReplaceAllLocation",
+                                                     NULL,
+                                                     0, 2, 1,
+                                                     MOUSEPAD_PARAM_READWRITE));
+
   /**
-   * MousepadPreferences:last-match-case
-   *
-   * Whether to enable match case in the search bar.
+   * Textview Preferences
    **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_MATCH_CASE,
-                                   g_param_spec_boolean ("last-match-case",
-                                                         "last-match-case",
-                                                         "last-match-case",
+                                   PROP_VIEW_AUTO_INDENT,
+                                   g_param_spec_boolean ("view-auto-indent",
+                                                         "ViewAutoIndent",
+                                                         NULL,
                                                          FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
-    /**
-   * MousepadPreferences:last-match-whole-word
-   *
-   * Whether to enable match case in the search bar.
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_MATCH_WHOLE_WORD,
-                                   g_param_spec_boolean ("last-match-whole-word",
-                                                         "last-match-whole-word",
-                                                         "last-match-whole-word",
+                                   PROP_VIEW_FONT_NAME,
+                                   g_param_spec_string ("view-font-name",
+                                                        "ViewFontName",
+                                                        NULL,
+                                                        NULL,
+                                                        MOUSEPAD_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_VIEW_LINE_NUMBERS,
+                                   g_param_spec_boolean ("view-line-numbers",
+                                                         "ViewLineNumbers",
+                                                         NULL,
                                                          FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:last-statusbar-visible
-   *
-   * Whether to display the statusbar in the Mousepad window.
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_STATUSBAR_VISIBLE,
-                                   g_param_spec_boolean ("last-statusbar-visible",
-                                                         "last-statusbar-visible",
-                                                         "last-statusbar-visible",
-                                                         TRUE,
+                                   PROP_VIEW_TAB_WIDTH,
+                                   g_param_spec_int ("view-tab-width",
+                                                     "ViewTabWidth",
+                                                     NULL,
+                                                     1, 32, 8,
+                                                     MOUSEPAD_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_VIEW_TABS_AS_SPACES,
+                                   g_param_spec_boolean ("view-insert-spaces",
+                                                         "ViewInsertSpaces",
+                                                         NULL,
+                                                         FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
+  g_object_class_install_property (gobject_class,
+                                   PROP_VIEW_WORD_WRAP,
+                                   g_param_spec_boolean ("view-word-wrap",
+                                                         "ViewWordWrap",
+                                                         NULL,
+                                                         FALSE,
+                                                         MOUSEPAD_PARAM_READWRITE));
+
+
   /**
-   * MousepadPreferences:last-window-height
-   *
-   * The last known height of a #MousepadWindow, which will be used as
-   * default height for newly created windows.
+   * Window Preferences
    **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_WINDOW_HEIGHT,
-                                   g_param_spec_int ("last-window-height",
-                                                     "last-window-height",
-                                                     "last-window-height",
+                                   PROP_WINDOW_HEIGHT,
+                                   g_param_spec_int ("window-height",
+                                                     "WindowHeight",
+                                                     NULL,
                                                      1, G_MAXINT, 480,
                                                      MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:last-window-width
-   *
-   * The last known width of a #MousepadWindow, which will be used as
-   * default width for newly created windows.
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_WINDOW_WIDTH,
-                                   g_param_spec_int ("last-window-width",
-                                                     "last-window-width",
-                                                     "last-window-width",
+                                   PROP_WINDOW_WIDTH,
+                                   g_param_spec_int ("window-width",
+                                                     "WindowWidth",
+                                                     NULL,
                                                      1, G_MAXINT, 640,
                                                      MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:last-word-wrap
-   *
-   * Whether word wrapping is enabled.
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_LAST_WORD_WRAP,
-                                   g_param_spec_boolean ("last-word-wrap",
-                                                         "last-word-wrap",
-                                                         "last-word-wrap",
-                                                         FALSE,
-                                                         MOUSEPAD_PARAM_READWRITE));
-
-  /**
-   * MousepadPreferences:last-wrap-around
-   *
-   * Whether to enable wrap around in the search bar.
-   **/
-  g_object_class_install_property (gobject_class,
-                                   PROP_LAST_WRAP_AROUND,
-                                   g_param_spec_boolean ("last-wrap-around",
-                                                         "last-wrap-around",
-                                                         "last-wrap-around",
+                                   PROP_WINDOW_STATUSBAR_VISIBLE,
+                                   g_param_spec_boolean ("window-statusbar-visible",
+                                                         "WindowStatusbarVisible",
+                                                         NULL,
                                                          TRUE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
+
   /**
-   * MousepadPreferences:misc-always-show-tabs
-   *
-   * Whether tabs are always visible. By default the tabs are hidden
-   * when there is only 1 screen opened.
+   * Hidden Preferences
    **/
   g_object_class_install_property (gobject_class,
                                    PROP_MISC_ALWAYS_SHOW_TABS,
                                    g_param_spec_boolean ("misc-always-show-tabs",
-                                                         "misc-always-show-tabs",
-                                                         "misc-always-show-tabs",
+                                                         "MiscAlwaysShowTabs",
+                                                         NULL,
                                                          FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:misc-cycle-tabs
-   *
-   * Whether to cycle tabs with the back and forward buttons in the go menu.
-   **/
   g_object_class_install_property (gobject_class,
                                    PROP_MISC_CYCLE_TABS,
                                    g_param_spec_boolean ("misc-cycle-tabs",
-                                                         "misc-cycle-tabs",
-                                                         "misc-cycle-tabs",
+                                                         "MiscCycleTabs",
+                                                         NULL,
                                                          FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:misc-show-full-path-in-title
-   *
-   * Whether to show the full file name path in the window title.
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_MISC_SHOW_FULL_PATH_IN_TITLE,
-                                   g_param_spec_boolean ("misc-show-full-path-in-title",
-                                                         "misc-show-full-path-in-title",
-                                                         "misc-show-full-path-in-title",
+                                   PROP_MISC_PATH_IN_TITLE,
+                                   g_param_spec_boolean ("misc-path-in-title",
+                                                         "MiscPathInTitle",
+                                                         NULL,
                                                          FALSE,
                                                          MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:misc-recent-menu-limit
-   *
-   * The number of items in the recent menu,
-   **/
   g_object_class_install_property (gobject_class,
-                                   PROP_MISC_RECENT_MENU_LIMIT,
-                                   g_param_spec_int ("misc-recent-menu-limit",
-                                                     "misc-recent-menu-limit",
-                                                     "misc-recent-menu-limit",
+                                   PROP_MISC_RECENT_MENU_ITEMS,
+                                   g_param_spec_int ("misc-recent-menu-items",
+                                                     "MiscRecentMenuItems",
+                                                     NULL,
                                                      1, G_MAXINT, 10,
                                                      MOUSEPAD_PARAM_READWRITE));
 
-  /**
-   * MousepadPreferences:misc-remember-geometry
-   *
-   * Whether Mousepad should remember the size of windows and apply this
-   * to newly created windows. If %TRUE the width and height are
-   * saved to "last-window-width" and "last-window-height". If
-   * %FALSE the user may specify the start size in "last-window-with"
-   * and "last-window-height".
-   **/
   g_object_class_install_property (gobject_class,
                                    PROP_MISC_REMEMBER_GEOMETRY,
                                    g_param_spec_boolean ("misc-remember-geometry",
-                                                         "misc-remember-geometry",
-                                                         "misc-remember-geometry",
+                                                         "MiscRememberGeometry",
+                                                         NULL,
                                                          TRUE,
                                                          MOUSEPAD_PARAM_READWRITE));
 }
@@ -401,7 +370,7 @@ mousepad_preferences_finalize (GObject *object)
   /* flush preferences */
   if (G_UNLIKELY (preferences->store_idle_id != 0))
     {
-      mousepad_preferences_store_idle (preferences);
+      mousepad_preferences_store (preferences);
       g_source_remove (preferences->store_idle_id);
     }
   /* release the property values */
@@ -424,6 +393,7 @@ mousepad_preferences_get_property (GObject    *object,
   GValue              *src;
 
   src = preferences->values + prop_id;
+
   if (G_IS_VALUE (src))
     g_value_copy (src, value);
   else
@@ -442,6 +412,7 @@ mousepad_preferences_set_property (GObject      *object,
   GValue              *dst;
 
   dst = preferences->values + prop_id;
+
   if (G_UNLIKELY (!G_IS_VALUE (dst)))
     {
       g_value_init (dst, pspec->value_type);
@@ -451,57 +422,68 @@ mousepad_preferences_set_property (GObject      *object,
   if (g_param_values_cmp (pspec, value, dst) != 0)
     {
       g_value_copy (value, dst);
-      mousepad_preferences_queue_store (preferences);
-    }
-}
-
 
-
-static void
-mousepad_preferences_queue_store (MousepadPreferences *preferences)
-{
-  if (preferences->store_idle_id == 0)
-    {
-      preferences->store_idle_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_preferences_store_idle,
-                                                    preferences, mousepad_preferences_store_idle_destroy);
+      mousepad_preferences_store (preferences);
     }
 }
 
 
 
-static gchar*
-property_name_to_option_name (const gchar *property_name)
+#ifndef NDEBUG
+static void
+mousepad_preferences_check_option_name (GParamSpec *pspec)
 {
   const gchar *s;
+  const gchar *name, *nick;
   gboolean     upper = TRUE;
   gchar       *option;
   gchar       *t;
 
-  option = g_new (gchar, strlen (property_name) + 1);
-  for (s = property_name, t = option; *s != '\0'; ++s)
+  /* get property name and nick */
+  name = g_param_spec_get_name (pspec);
+  nick = g_param_spec_get_nick (pspec);
+
+  if (G_UNLIKELY (nick == NULL))
     {
-      if (*s == '-')
-        {
-          upper = TRUE;
-        }
-      else if (upper)
-        {
-          *t++ = g_ascii_toupper (*s);
-          upper = FALSE;
-        }
-      else
+      g_warning ("The nick name of %s is NULL", name);
+    }
+  else
+    {
+      /* allocate string for option name */
+      option = g_new (gchar, strlen (name) + 1);
+
+      /* convert name */
+      for (s = name, t = option; *s != '\0'; ++s)
         {
-          *t++ = *s;
+          if (*s == '-')
+            {
+              upper = TRUE;
+            }
+          else if (upper)
+            {
+              *t++ = g_ascii_toupper (*s);
+              upper = FALSE;
+            }
+          else
+            {
+              *t++ = *s;
+            }
         }
-    }
-  *t = '\0';
+      *t = '\0';
 
-  return option;
+      /* compare the strings */
+      if (G_UNLIKELY (!option || strcmp (option, nick) != 0))
+        g_warning ("The option name (%s) and nick name (%s) of property \"%s\" do not match", option, nick, name);
+
+      /* cleanup */
+      g_free (option);
+    }
 }
+#endif
 
 
 
-static gboolean
+static void
 mousepad_preferences_load (MousepadPreferences *preferences)
 {
   const gchar  *string;
@@ -510,43 +492,54 @@ mousepad_preferences_load (MousepadPreferences *preferences)
   XfceRc       *rc;
   GValue        dst = { 0, };
   GValue        src = { 0, };
-  gchar        *option;
   guint         nspecs;
   guint         n;
 
+  /* try to open the config file */
   rc = xfce_rc_config_open (XFCE_RESOURCE_CONFIG, "Mousepad/mousepadrc", TRUE);
   if (G_UNLIKELY (rc == NULL))
     {
-      g_warning ("Failed to load the preferences. Unable to open \"~/.config/Mousepad/mousepadrc\"");
-      return FALSE;
+      g_warning (_("Failed to load the preferences."));
+
+      return;
     }
 
+  /* freeze notification signals */
   g_object_freeze_notify (G_OBJECT (preferences));
 
+  /* set group */
   xfce_rc_set_group (rc, "Configuration");
 
+  /* get all the properties in the class */
   specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (preferences), &nspecs);
 
   for (n = 0; n < nspecs; ++n)
     {
       spec = specs[n];
 
-      option = property_name_to_option_name (spec->name);
-      string = xfce_rc_read_entry (rc, option, NULL);
-      g_free (option);
+#ifndef NDEBUG
+      /* check nick name with generated option name */
+      mousepad_preferences_check_option_name (spec);
+#endif
+
+      /* read the entry */
+      string = xfce_rc_read_entry (rc, spec->_nick, NULL);
 
       if (G_UNLIKELY (string == NULL))
         continue;
 
+      /* create gvalue with the string as value */
       g_value_init (&src, G_TYPE_STRING);
       g_value_set_static_string (&src, string);
 
       if (spec->value_type == G_TYPE_STRING)
         {
+          /* they have the same type, so set the property */
           g_object_set_property (G_OBJECT (preferences), spec->name, &src);
         }
       else if (g_value_type_transformable (G_TYPE_STRING, spec->value_type))
         {
+          /* transform the type */
           g_value_init (&dst, spec->value_type);
           if (g_value_transform (&src, &dst))
             g_object_set_property (G_OBJECT (preferences), spec->name, &dst);
@@ -557,15 +550,30 @@ mousepad_preferences_load (MousepadPreferences *preferences)
           g_warning ("Failed to load property \"%s\"", spec->name);
         }
 
+      /* cleanup */
       g_value_unset (&src);
     }
+
+  /* cleanup the specs */
   g_free (specs);
 
+  /* close the rc file */
   xfce_rc_close (rc);
 
+  /* allow notifications again */
   g_object_thaw_notify (G_OBJECT (preferences));
+}
 
-  return FALSE;
+
+
+static void
+mousepad_preferences_store (MousepadPreferences *preferences)
+{
+  if (preferences->store_idle_id == 0)
+    {
+      preferences->store_idle_id = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) mousepad_preferences_store_idle,
+                                                    preferences, mousepad_preferences_store_idle_destroy);
+    }
 }
 
 
@@ -580,55 +588,56 @@ mousepad_preferences_store_idle (gpointer user_data)
   XfceRc              *rc;
   GValue               dst = { 0, };
   GValue               src = { 0, };
-  gchar               *option;
   guint                nspecs;
   guint                n;
 
+  /* open the config file */
   rc = xfce_rc_config_open (XFCE_RESOURCE_CONFIG, "Mousepad/mousepadrc", FALSE);
   if (G_UNLIKELY (rc == NULL))
     {
-      g_warning ("Failed to store the preferences. Unable to open \"~/.config/Mousepad/mousepadrc\"");
+      g_warning (_("Failed to store the preferences."));
       return FALSE;
     }
 
+  /* set the group */
   xfce_rc_set_group (rc, "Configuration");
 
+  /* get the list of properties in the class */
   specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (preferences), &nspecs);
 
   for (n = 0; n < nspecs; ++n)
     {
       spec = specs[n];
 
+      /* init a string value */
       g_value_init (&dst, G_TYPE_STRING);
 
       if (spec->value_type == G_TYPE_STRING)
         {
+          /* set the string value */
           g_object_get_property (G_OBJECT (preferences), spec->name, &dst);
         }
       else
         {
+          /* property contains another type, transform it first */
           g_value_init (&src, spec->value_type);
           g_object_get_property (G_OBJECT (preferences), spec->name, &src);
           g_value_transform (&src, &dst);
           g_value_unset (&src);
         }
 
-      /* determine the option name for the spec */
-      option = property_name_to_option_name (spec->name);
-
       /* store the setting */
       string = g_value_get_string (&dst);
       if (G_LIKELY (string != NULL))
-        xfce_rc_write_entry (rc, option, string);
+        xfce_rc_write_entry (rc, spec->_nick, string);
 
       /* cleanup */
       g_value_unset (&dst);
-      g_free (option);
     }
 
   /* cleanup */
-  xfce_rc_close (rc);
   g_free (specs);
+  xfce_rc_close (rc);
 
   return FALSE;
 }
@@ -661,8 +670,7 @@ mousepad_preferences_get (void)
   if (G_UNLIKELY (preferences == NULL))
     {
       preferences = g_object_new (MOUSEPAD_TYPE_PREFERENCES, NULL);
-      g_object_add_weak_pointer (G_OBJECT (preferences),
-                                 (gpointer) &preferences);
+      g_object_add_weak_pointer (G_OBJECT (preferences), (gpointer) &preferences);
     }
   else
     {
diff --git a/mousepad/mousepad-print.c b/mousepad/mousepad-print.c
new file mode 100644
index 0000000..532103d
--- /dev/null
+++ b/mousepad/mousepad-print.c
@@ -0,0 +1,277 @@
+/* $Id$ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-print.h>
+
+
+
+static void mousepad_print_class_init (MousepadPrintClass *klass);
+static void mousepad_print_init (MousepadPrint *print);
+static void mousepad_print_finalize (GObject *object);
+static void mousepad_print_begin_print (GtkPrintOperation *operation, GtkPrintContext   *context);
+static void mousepad_print_draw_page (GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr);
+static void mousepad_print_end_print (GtkPrintOperation *operation, GtkPrintContext *context);
+static GtkWidget *mousepad_print_create_custom_widget (GtkPrintOperation *operation);
+static void mousepad_print_status_changed (GtkPrintOperation *operation);
+
+
+
+struct _MousepadPrintClass
+{
+  GtkPrintOperationClass __parent__;
+};
+
+struct _MousepadPrint
+{
+  GtkPrintOperation __parent__;
+
+  /* the document we're going to print */
+  MousepadDocument    *document;
+
+  /* calculated lines per page */
+  gint                 lines_per_page;
+};
+
+
+
+static GObjectClass *mousepad_print_parent_class;
+
+
+
+GType
+mousepad_print_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+
+  if (G_UNLIKELY (type == G_TYPE_INVALID))
+    {
+      type = g_type_register_static_simple (GTK_TYPE_PRINT_OPERATION,
+                                            I_("MousepadPrint"),
+                                            sizeof (MousepadPrintClass),
+                                            (GClassInitFunc) mousepad_print_class_init,
+                                            sizeof (MousepadPrint),
+                                            (GInstanceInitFunc) mousepad_print_init,
+                                            0);
+    }
+
+  return type;
+}
+
+
+
+static void
+mousepad_print_class_init (MousepadPrintClass *klass)
+{
+  GObjectClass           *gobject_class;
+  GtkPrintOperationClass *gtkprintoperation_class;
+
+  mousepad_print_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = mousepad_print_finalize;
+
+  gtkprintoperation_class = GTK_PRINT_OPERATION_CLASS (klass);
+  gtkprintoperation_class->begin_print = mousepad_print_begin_print;
+  gtkprintoperation_class->draw_page = mousepad_print_draw_page;
+  gtkprintoperation_class->end_print = mousepad_print_end_print;
+  gtkprintoperation_class->create_custom_widget = mousepad_print_create_custom_widget;
+  gtkprintoperation_class->status_changed = mousepad_print_status_changed;
+}
+
+
+
+static void
+mousepad_print_init (MousepadPrint *print)
+{
+  /* set a custom tab label */
+  //gtk_print_operation_set_custom_tab_label (GTK_PRINT_OPERATION (print), _("Document Settings"));
+}
+
+
+
+static void
+mousepad_print_finalize (GObject *object)
+{
+  (*G_OBJECT_CLASS (mousepad_print_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_print_begin_print (GtkPrintOperation *operation,
+                            GtkPrintContext   *context)
+{
+  MousepadPrint *print = MOUSEPAD_PRINT (operation);
+  gint           n_lines, font_height;
+  gint           page_height, n_pages;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_PRINT (print));
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (print->document));
+  _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (print->document->buffer));
+
+  /* get the document font information */
+  mousepad_document_get_font_information (print->document, NULL, &font_height);
+
+  /* get the number of lines in the buffer */
+  n_lines = gtk_text_buffer_get_line_count (print->document->buffer);
+
+  /* get the page height */
+  page_height = gtk_print_context_get_height (context);
+
+  /* lines per page */
+  print->lines_per_page = page_height / font_height;
+
+  /* calculate the number of pages */
+  n_pages = (n_lines / print->lines_per_page) + 1;
+
+  /* set the number of pages */
+  gtk_print_operation_set_n_pages (operation, n_pages);
+}
+
+
+
+static void
+mousepad_print_draw_page (GtkPrintOperation *operation,
+                          GtkPrintContext   *context,
+                          gint               page_nr)
+{
+  MousepadPrint        *print = MOUSEPAD_PRINT (operation);
+  PangoLayout          *layout;
+  PangoLayoutLine      *line;
+  PangoFontDescription *font_desc;
+  gint                  start, end;
+  gint                  i, font_height;
+  GtkTextIter           start_iter, end_iter;
+  MousepadDocument     *document = print->document;
+  gchar                *text;
+  cairo_t              *cr;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_PRINT (print));
+
+  /* start and end line in the buffer */
+  start = print->lines_per_page * page_nr;
+  end = start + print->lines_per_page - 1;
+
+  /* get the iters */
+  gtk_text_buffer_get_iter_at_line (document->buffer, &start_iter, start);
+  gtk_text_buffer_get_iter_at_line (document->buffer, &end_iter, end);
+  gtk_text_iter_forward_to_line_end (&end_iter);
+
+  /* create the pango layout */
+  layout = gtk_print_context_create_pango_layout (context);
+
+  /* get the document font information */
+  mousepad_document_get_font_information (document, &font_desc, &font_height);
+
+  /* set the layout font description */
+  pango_layout_set_font_description (layout, font_desc);
+
+  /* get the text slice */
+  text = gtk_text_buffer_get_slice (document->buffer, &start_iter, &end_iter, TRUE);
+
+  /* set the pango layout text */
+  pango_layout_set_text (layout, text, -1);
+
+  /* cleanup */
+  g_free (text);
+
+  /* get the cairo context */
+  cr = gtk_print_context_get_cairo_context (context);
+
+  /* show all the lines at the correct position */
+  for (i = 0; i < pango_layout_get_line_count (layout); i++)
+    {
+      /* get the line */
+      line = pango_layout_get_line_readonly (layout, i);
+
+      /* move the cairo position */
+      cairo_move_to (cr, 0, font_height * (i + 1));
+
+      /* show the line at the new position */
+      pango_cairo_show_layout_line (cr, line);
+    }
+
+  /* release the layout */
+  g_object_unref (G_OBJECT (layout));
+}
+
+
+
+static void
+mousepad_print_end_print (GtkPrintOperation *operation,
+                          GtkPrintContext   *context)
+{
+
+}
+
+
+
+static GtkWidget *
+mousepad_print_create_custom_widget (GtkPrintOperation *operation)
+{
+  return NULL;
+}
+
+
+
+static void
+mousepad_print_status_changed (GtkPrintOperation *operation)
+{
+
+}
+
+
+
+MousepadPrint *
+mousepad_print_new (void)
+{
+  return g_object_new (MOUSEPAD_TYPE_PRINT, NULL);
+}
+
+
+
+gboolean
+mousepad_print_document_interactive (MousepadPrint     *print,
+                                     MousepadDocument  *document,
+                                     GtkWindow         *parent,
+                                     GError           **error)
+{
+  GtkPrintOperationResult result;
+
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_PRINT (print), FALSE);
+  _mousepad_return_val_if_fail (GTK_IS_PRINT_OPERATION (print), FALSE);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+  _mousepad_return_val_if_fail (GTK_IS_WINDOW (parent), FALSE);
+  _mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  /* set the document */
+  print->document = document;
+
+  /* run the operation */
+  result = gtk_print_operation_run (GTK_PRINT_OPERATION (print),
+                                    GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+                                    parent, error);
+
+  return (result != GTK_PRINT_OPERATION_RESULT_ERROR);
+}
diff --git a/mousepad/mousepad-print.h b/mousepad/mousepad-print.h
new file mode 100644
index 0000000..84273db
--- /dev/null
+++ b/mousepad/mousepad-print.h
@@ -0,0 +1,44 @@
+/* $Id: mousepad-print.h 25747 2007-05-22 19:50:01Z nick $ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __MOUSEPAD_PRINT_H__
+#define __MOUSEPAD_PRINT_H__
+
+G_BEGIN_DECLS
+
+typedef struct _MousepadPrintClass MousepadPrintClass;
+typedef struct _MousepadPrint      MousepadPrint;
+
+#define MOUSEPAD_TYPE_PRINT            (mousepad_print_get_type ())
+#define MOUSEPAD_PRINT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_PRINT, MousepadPrint))
+#define MOUSEPAD_PRINT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_PRINT, MousepadPrintClass))
+#define MOUSEPAD_IS_PRINT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_PRINT))
+#define MOUSEPAD_IS_PRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_PRINT))
+#define MOUSEPAD_PRINT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_PRINT, MousepadPrintClass))
+
+GType          mousepad_print_get_type (void) G_GNUC_CONST;
+
+MousepadPrint *mousepad_print_new (void);
+
+gboolean mousepad_print_document_interactive (MousepadPrint     *print,
+                                     MousepadDocument  *document,
+                                     GtkWindow         *parent,
+                                     GError           **error);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_PRINT_H__ */
diff --git a/mousepad/mousepad-private.h b/mousepad/mousepad-private.h
index 0537b2c..ee4da88 100644
--- a/mousepad/mousepad-private.h
+++ b/mousepad/mousepad-private.h
@@ -25,30 +25,18 @@
 
 G_BEGIN_DECLS
 
-/* test timers */
-#define TIMER_START \
-  GTimer *__FUNCTION__timer = g_timer_new();
-
-#define TIMER_SPLIT \
-  g_print ("%s (%s:%d): %.2f ms\n", __FUNCTION__, __FILE__, __LINE__, g_timer_elapsed (__FUNCTION__timer, NULL) * 1000);
-
-#define TIMER_STOP \
-  TIMER_SPLIT \
-  g_timer_destroy (__FUNCTION__timer);
-
-#define PRINT_LINE g_print ("%d\n", __LINE__);
-
+/* for personal testing */
+#define TIMER_START  GTimer *__FUNCTION__timer = g_timer_new();
+#define TIMER_SPLIT  g_print ("%s (%s:%d): %.2f ms\n", __FUNCTION__, __FILE__, __LINE__, g_timer_elapsed (__FUNCTION__timer, NULL) * 1000);
+#define TIMER_STOP   TIMER_SPLIT g_timer_destroy (__FUNCTION__timer);
+#define PRINT_LINE   g_print ("%d\n", __LINE__);
 
 /* optimize the properties */
 #define MOUSEPAD_PARAM_READWRITE (G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
 
-
-
 /* support for canonical strings */
 #define I_(string) (g_intern_static_string ((string)))
 
-
-
 /* support macros for debugging */
 #ifndef NDEBUG
 #define _mousepad_assert(expr)                  g_assert (expr)
@@ -62,13 +50,26 @@ G_BEGIN_DECLS
 #define _mousepad_return_val_if_fail(expr, val) G_STMT_START{ (void)0; }G_STMT_END
 #endif
 
-
-
 /* avoid trivial g_value_get_*() function calls */
 #ifdef NDEBUG
-#define g_value_get_string(v)  (((const GValue *) (v))->data[0].v_pointer)
-#define g_value_get_boxed(v)   (((const GValue *) (v))->data[0].v_pointer)
-#define g_value_get_pointer(v) (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_boolean(v)  (((const GValue *) (v))->data[0].v_int)
+#define g_value_get_char(v)     (((const GValue *) (v))->data[0].v_int)
+#define g_value_get_uchar(v)    (((const GValue *) (v))->data[0].v_uint)
+#define g_value_get_int(v)      (((const GValue *) (v))->data[0].v_int)
+#define g_value_get_uint(v)     (((const GValue *) (v))->data[0].v_uint)
+#define g_value_get_long(v)     (((const GValue *) (v))->data[0].v_long)
+#define g_value_get_ulong(v)    (((const GValue *) (v))->data[0].v_ulong)
+#define g_value_get_int64(v)    (((const GValue *) (v))->data[0].v_int64)
+#define g_value_get_uint64(v)   (((const GValue *) (v))->data[0].v_uint64)
+#define g_value_get_enum(v)     (((const GValue *) (v))->data[0].v_long)
+#define g_value_get_flags(v)    (((const GValue *) (v))->data[0].v_ulong)
+#define g_value_get_float(v)    (((const GValue *) (v))->data[0].v_float)
+#define g_value_get_double(v)   (((const GValue *) (v))->data[0].v_double)
+#define g_value_get_string(v)   (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_param(v)    (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_boxed(v)    (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_pointer(v)  (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_object(v)   (((const GValue *) (v))->data[0].v_pointer)
 #endif
 
 G_END_DECLS
diff --git a/mousepad/mousepad-replace-dialog.c b/mousepad/mousepad-replace-dialog.c
new file mode 100644
index 0000000..9057766
--- /dev/null
+++ b/mousepad/mousepad-replace-dialog.c
@@ -0,0 +1,665 @@
+/* $Id$ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-replace-dialog.h>
+#include <mousepad/mousepad-dialogs.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-marshal.h>
+
+
+
+static void                 mousepad_replace_dialog_class_init              (MousepadReplaceDialogClass *klass);
+static void                 mousepad_replace_dialog_init                    (MousepadReplaceDialog      *dialog);
+static void                 mousepad_replace_dialog_unrealize               (GtkWidget                  *widget);
+static void                 mousepad_replace_dialog_finalize                (GObject                    *object);
+static void                 mousepad_replace_dialog_response                (GtkWidget                  *widget,
+                                                                             gint                        response_id);
+static void                 mousepad_replace_dialog_changed                 (MousepadReplaceDialog      *dialog);
+static void                 mousepad_replace_dialog_case_sensitive_toggled  (GtkToggleButton            *button,
+                                                                             MousepadReplaceDialog      *dialog);
+static void                 mousepad_replace_dialog_whole_word_toggled      (GtkToggleButton            *button,
+                                                                             MousepadReplaceDialog      *dialog);
+static void                 mousepad_replace_dialog_replace_all_toggled     (GtkToggleButton            *button,
+                                                                             MousepadReplaceDialog      *dialog);
+static void                 mousepad_replace_dialog_search_location_changed (GtkComboBox                *combo,
+                                                                             MousepadReplaceDialog      *dialog);
+static void                 mousepad_replace_dialog_direction_changed       (GtkComboBox                *combo,
+                                                                             MousepadReplaceDialog      *dialog);
+static void                 mousepad_replace_dialog_history_combo_box       (GtkComboBox                *combo_box);
+static void                 mousepad_replace_dialog_history_insert_text     (const gchar                *text);
+
+
+
+struct _MousepadReplaceDialogClass
+{
+  GtkDialogClass __parent__;
+};
+
+struct _MousepadReplaceDialog
+{
+  GtkDialog __parent__;
+
+  /* mousepad preferences */
+  MousepadPreferences *preferences;
+
+  /* dialog widgets */
+  GtkWidget           *search_entry;
+  GtkWidget           *replace_entry;
+  GtkWidget           *find_button;
+  GtkWidget           *replace_button;
+  GtkWidget           *search_location_combo;
+  GtkWidget           *hits_label;
+
+  /* search flags */
+  guint                search_direction;
+  guint                match_case : 1;
+  guint                match_whole_word : 1;
+  guint                replace_all : 1;
+  guint                replace_all_location;
+};
+
+enum
+{
+  IN_SELECTION = 0,
+  IN_DOCUMENT,
+  IN_ALL_DOCUMENTS
+};
+
+enum
+{
+  DIRECTION_UP = 0,
+  DIRECTION_DOWN,
+  DIRECTION_BOTH
+};
+
+enum
+{
+  SEARCH,
+  LAST_SIGNAL
+};
+
+
+
+static GObjectClass *mousepad_replace_dialog_parent_class;
+static GSList       *history_list = NULL;
+static guint         dialog_signals[LAST_SIGNAL];
+
+
+
+GType
+mousepad_replace_dialog_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+
+  if (G_UNLIKELY (type == G_TYPE_INVALID))
+    {
+      type = g_type_register_static_simple (GTK_TYPE_DIALOG,
+                                            I_("MousepadReplaceDialog"),
+                                            sizeof (MousepadReplaceDialogClass),
+                                            (GClassInitFunc) mousepad_replace_dialog_class_init,
+                                            sizeof (MousepadReplaceDialog),
+                                            (GInstanceInitFunc) mousepad_replace_dialog_init,
+                                            0);
+    }
+
+  return type;
+}
+
+
+
+static void
+mousepad_replace_dialog_class_init (MousepadReplaceDialogClass *klass)
+{
+  GObjectClass   *gobject_class;
+  GtkWidgetClass *gtkwidget_class;
+
+  mousepad_replace_dialog_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = mousepad_replace_dialog_finalize;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->unrealize = mousepad_replace_dialog_unrealize;
+
+  dialog_signals[SEARCH] =
+    g_signal_new (I_("search"),
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_NO_HOOKS,
+                  0, NULL, NULL,
+                  _mousepad_marshal_INT__FLAGS_STRING_STRING,
+                  G_TYPE_INT, 3,
+                  MOUSEPAD_TYPE_SEARCH_FLAGS,
+                  G_TYPE_STRING, G_TYPE_STRING);
+}
+
+
+
+static void
+mousepad_replace_dialog_init (MousepadReplaceDialog *dialog)
+{
+  GtkWidget    *vbox, *hbox, *combo, *label, *check;
+  GtkSizeGroup *size_group;
+  gboolean      match_whole_word, match_case;
+  gint          search_direction, replace_all_location;
+
+  /* initialize some variables */
+  dialog->replace_all = FALSE;
+
+  /* get the mousepad preferences */
+  dialog->preferences = mousepad_preferences_get ();
+
+  /* read the preferences */
+  g_object_get (G_OBJECT (dialog->preferences),
+                "search-match-whole-word", &match_whole_word,
+                "search-match-case", &match_case,
+                "search-direction", &search_direction,
+                "search-replace-all-location", &replace_all_location,
+                NULL);
+
+  /* set some flags */
+  dialog->match_whole_word = match_whole_word;
+  dialog->match_case = match_case;
+  dialog->search_direction = search_direction;
+  dialog->replace_all_location = replace_all_location;
+
+  /* set dialog properties */
+  gtk_window_set_title (GTK_WINDOW (dialog), _("Replace"));
+  gtk_window_set_default_size (GTK_WINDOW (dialog), 400, -1);
+  gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+  g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (mousepad_replace_dialog_response), NULL);
+
+  /* dialog buttons */
+  dialog->find_button = gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_FIND, MOUSEPAD_RESPONSE_FIND);
+  dialog->replace_button = mousepad_util_image_button (GTK_STOCK_FIND_AND_REPLACE, _("_Replace"));
+  gtk_dialog_add_action_widget (GTK_DIALOG (dialog), dialog->replace_button, MOUSEPAD_RESPONSE_REPLACE);
+  gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, MOUSEPAD_RESPONSE_CLOSE);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_FIND);
+
+  /* create main vertical box */
+  vbox = g_object_new (GTK_TYPE_VBOX, "border-width", 6, "spacing", 4, NULL);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
+  gtk_widget_show (vbox);
+
+  /* horizontal box for search string */
+  hbox = gtk_hbox_new (FALSE, 8);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+  gtk_widget_show (hbox);
+
+  /* create a size group */
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+  label = gtk_label_new_with_mnemonic (_("_Search for:"));
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_size_group_add_widget (size_group, label);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+  gtk_widget_show (label);
+
+  combo = gtk_combo_box_entry_new_text ();
+  mousepad_replace_dialog_history_combo_box (GTK_COMBO_BOX (combo));
+  gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL(label), combo);
+  gtk_widget_show (combo);
+
+  /* store as an entry widget */
+  dialog->search_entry = gtk_bin_get_child (GTK_BIN (combo));
+  g_signal_connect_swapped (G_OBJECT (dialog->search_entry), "changed", G_CALLBACK (mousepad_replace_dialog_changed), dialog);
+
+  /* horizontal box for replace string */
+  hbox = gtk_hbox_new (FALSE, 8);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic (_("Replace _with:"));
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_size_group_add_widget (size_group, label);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+  gtk_widget_show (label);
+
+  combo = gtk_combo_box_entry_new_text ();
+  mousepad_replace_dialog_history_combo_box (GTK_COMBO_BOX (combo));
+  gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+  gtk_widget_show (combo);
+
+  /* store as an entry widget */
+  dialog->replace_entry = gtk_bin_get_child (GTK_BIN (combo));
+
+  /* search direction */
+  hbox = gtk_hbox_new (FALSE, 8);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL(label), combo);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic (_("Search _direction:"));
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_size_group_add_widget (size_group, label);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+  gtk_widget_show (label);
+
+  combo = gtk_combo_box_new_text ();
+  gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL(label), combo);
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Up"));
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Down"));
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Both"));
+  gtk_combo_box_set_active (GTK_COMBO_BOX (combo), search_direction);
+  g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (mousepad_replace_dialog_direction_changed), dialog);
+  gtk_widget_show (combo);
+
+  /* release size group */
+  g_object_unref (G_OBJECT (size_group));
+
+  /* case sensitive */
+  check = gtk_check_button_new_with_mnemonic (_("Case sensi_tive"));
+  gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), match_case);
+  g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_replace_dialog_case_sensitive_toggled), dialog);
+  gtk_widget_show (check);
+
+  /* match whole word */
+  check = gtk_check_button_new_with_mnemonic (_("_Match whole word"));
+  gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), match_whole_word);
+  g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_replace_dialog_whole_word_toggled), dialog);
+  gtk_widget_show (check);
+
+  /* horizontal box for the replace all options */
+  hbox = gtk_hbox_new (FALSE, 8);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  check = gtk_check_button_new_with_mnemonic (_("Replace _all in:"));
+  gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+  g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_replace_dialog_replace_all_toggled), dialog);
+  gtk_widget_show (check);
+
+  combo = dialog->search_location_combo = gtk_combo_box_new_text ();
+  gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Selection"));
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Document"));
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("All Documents"));
+  gtk_combo_box_set_active (GTK_COMBO_BOX (combo), replace_all_location);
+  gtk_widget_set_sensitive (combo, FALSE);
+  g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (mousepad_replace_dialog_search_location_changed), dialog);
+  gtk_widget_show (combo);
+
+  label = dialog->hits_label = gtk_label_new (NULL);
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+}
+
+
+
+static void
+mousepad_replace_dialog_unrealize (GtkWidget *widget)
+{
+  MousepadReplaceDialog *dialog = MOUSEPAD_REPLACE_DIALOG (widget);
+  const gchar           *text;
+
+  _mousepad_return_if_fail (GTK_IS_ENTRY (dialog->replace_entry));
+  _mousepad_return_if_fail (GTK_IS_ENTRY (dialog->search_entry));
+
+  text = gtk_entry_get_text (GTK_ENTRY (dialog->search_entry));
+  mousepad_replace_dialog_history_insert_text (text);
+
+  text = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
+  mousepad_replace_dialog_history_insert_text (text);
+
+  (*GTK_WIDGET_CLASS (mousepad_replace_dialog_parent_class)->unrealize) (widget);
+}
+
+
+
+static void
+mousepad_replace_dialog_finalize (GObject *object)
+{
+  MousepadReplaceDialog *dialog = MOUSEPAD_REPLACE_DIALOG (object);
+
+  /* release the preferences */
+  g_object_unref (G_OBJECT (dialog->preferences));
+
+  (*G_OBJECT_CLASS (mousepad_replace_dialog_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_replace_dialog_response (GtkWidget *widget,
+                                  gint       response_id)
+{
+  MousepadSearchFlags    flags;
+  MousepadReplaceDialog *dialog = MOUSEPAD_REPLACE_DIALOG (widget);
+  gint                   matches;
+  const gchar           *search_str, *replace_str;
+  gchar                 *hits_str;
+
+  /* close dialog */
+  if (response_id == MOUSEPAD_RESPONSE_CLOSE)
+    goto destroy_dialog;
+
+  /* search direction */
+  if (dialog->search_direction == DIRECTION_UP
+      && dialog->replace_all == FALSE)
+    flags = MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD;
+  else
+    flags = MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD;
+
+  /* case sensitive searching */
+  if (dialog->match_case)
+    flags |= MOUSEPAD_SEARCH_FLAGS_MATCH_CASE;
+
+  /* only match whole words */
+  if (dialog->match_whole_word)
+    flags |= MOUSEPAD_SEARCH_FLAGS_WHOLE_WORD;
+
+  /* wrap around */
+  if (dialog->search_direction == DIRECTION_BOTH
+      && dialog->replace_all == FALSE)
+    flags |= MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND;
+
+  /* search area */
+  if (dialog->replace_all
+      && dialog->replace_all_location == IN_SELECTION)
+    flags |= MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION;
+  else
+    flags |= MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT;
+
+  /* start position */
+  if (response_id == MOUSEPAD_RESPONSE_CHECK_ENTRY)
+    {
+      /* no visible actions */
+      flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_NONE;
+
+      if (dialog->replace_all)
+        goto replace_flags;
+      else
+        goto search_flags;
+    }
+  else if (response_id == MOUSEPAD_RESPONSE_FIND)
+    {
+      /* select the first match */
+      flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT;
+
+      search_flags:
+
+      /* start at the 'end' of the selection */
+      if (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD)
+        flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START;
+      else
+        flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END;
+    }
+  else if (response_id == MOUSEPAD_RESPONSE_REPLACE)
+    {
+      /* replace matches */
+      flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE;
+
+      if (dialog->replace_all)
+        {
+          replace_flags:
+
+          /* replace all from the beginning of the document */
+          flags |= MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START
+                   | MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA;
+
+          /* search all opened documents (flag used in mousepad-window.c) */
+          if (dialog->replace_all_location == IN_ALL_DOCUMENTS)
+            flags |= MOUSEPAD_SEARCH_FLAGS_ALL_DOCUMENTS;
+        }
+      else
+        {
+          /* start at the 'beginning' of the selection */
+          if (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD)
+            flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END;
+          else
+            flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START;
+        }
+    }
+  else
+    {
+      destroy_dialog:
+
+      /* destroy the window */
+      gtk_widget_destroy (widget);
+
+      /* leave */
+      return;
+    }
+
+  /* get strings */
+  search_str = gtk_entry_get_text (GTK_ENTRY (dialog->search_entry));
+  replace_str = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
+
+  /* emit the signal */
+  g_signal_emit (G_OBJECT (dialog), dialog_signals[SEARCH], 0, flags, search_str, replace_str, &matches);
+
+  /* set search widget result */
+  mousepad_util_entry_error (dialog->search_entry, matches == 0);
+
+  /* update hits counter */
+  if (dialog->replace_all)
+    {
+      hits_str = g_strdup_printf ("%d %s", matches, matches == 1 ? _("occurence") : _("occurences"));
+      gtk_label_set_markup (GTK_LABEL (dialog->hits_label), hits_str);
+      g_free (hits_str);
+    }
+}
+
+
+static void
+mousepad_replace_dialog_changed (MousepadReplaceDialog *dialog)
+{
+  const gchar *text;
+  gboolean     sensitive;
+
+  /* get the search entry text */
+  text = gtk_entry_get_text (GTK_ENTRY (dialog->search_entry));
+
+  if (text != NULL && *text != '\0')
+    {
+      /* do an invisible search to give the user some visible feedback */
+      gtk_dialog_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_CHECK_ENTRY);
+
+      /* buttons are sensitive */
+      sensitive = TRUE;
+    }
+  else
+    {
+      /* not text, means no error */
+      mousepad_util_entry_error (dialog->search_entry, FALSE);
+
+      /* reset occurences label */
+      gtk_label_set_text (GTK_LABEL (dialog->hits_label), NULL);
+
+      /* buttons are not sensitive */
+      sensitive = FALSE;
+    }
+
+  /* set the sensitivity */
+  gtk_widget_set_sensitive (dialog->find_button, sensitive);
+  gtk_widget_set_sensitive (dialog->replace_button, sensitive);
+}
+
+
+
+static void
+mousepad_replace_dialog_case_sensitive_toggled (GtkToggleButton       *button,
+                                                MousepadReplaceDialog *dialog)
+{
+  /* get the toggle button state */
+  dialog->match_case = gtk_toggle_button_get_active (button);
+
+  /* save the setting */
+  g_object_set (G_OBJECT (dialog->preferences), "search-match-case", dialog->match_case, NULL);
+
+  /* update dialog */
+  mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_whole_word_toggled (GtkToggleButton       *button,
+                                            MousepadReplaceDialog *dialog)
+{
+  /* get the toggle button state */
+  dialog->match_whole_word = gtk_toggle_button_get_active (button);
+
+  /* save the setting */
+  g_object_set (G_OBJECT (dialog->preferences), "search-match-whole-word", dialog->match_whole_word, NULL);
+
+  /* update dialog */
+  mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_replace_all_toggled (GtkToggleButton       *button,
+                                             MousepadReplaceDialog *dialog)
+{
+  gboolean active;
+
+  /* get the toggle button state */
+  active = gtk_toggle_button_get_active (button);
+
+  /* save flags */
+  dialog->replace_all = active;
+
+  /* set the sensitivity of some dialog widgets */
+  gtk_widget_set_sensitive (dialog->search_location_combo, active);
+
+  /* reset occurences label */
+  gtk_label_set_text (GTK_LABEL (dialog->hits_label), NULL);
+
+  /* set new label of the replace button */
+  gtk_button_set_label (GTK_BUTTON (dialog->replace_button),
+                        active ? _("_Replace All") : _("_Replace"));
+
+  /* update dialog */
+  mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_search_location_changed (GtkComboBox           *combo,
+                                                 MousepadReplaceDialog *dialog)
+{
+  /* get the selected action */
+  dialog->replace_all_location = gtk_combo_box_get_active (combo);
+
+  /* save setting */
+  g_object_set (G_OBJECT (dialog->preferences), "search-replace-all-location", dialog->replace_all_location, NULL);
+
+  /* update dialog */
+  mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_direction_changed (GtkComboBox           *combo,
+                                           MousepadReplaceDialog *dialog)
+{
+  /* get the selected item */
+  dialog->search_direction = gtk_combo_box_get_active (combo);
+
+  /* save the last direction */
+  g_object_set (G_OBJECT (dialog->preferences), "search-direction", dialog->search_direction, NULL);
+
+  /* update dialog */
+  mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+/**
+ * History functions
+ **/
+static void
+mousepad_replace_dialog_history_combo_box (GtkComboBox *combo_box)
+{
+  GSList *li;
+
+  _mousepad_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+
+  /* append the items from the history to the combobox */
+  for (li = history_list; li != NULL; li = li->next)
+    gtk_combo_box_append_text (combo_box, li->data);
+}
+
+
+
+static void
+mousepad_replace_dialog_history_insert_text (const gchar *text)
+{
+  GSList *li;
+
+  /* quit if the box is empty */
+  if (text == NULL || *text == '\0')
+    return;
+
+  /* check if the string is already in the history */
+  for (li = history_list; li != NULL; li = li->next)
+    if (strcmp (li->data, text) == 0)
+      return;
+
+  /* prepend the string */
+  history_list = g_slist_prepend (history_list, g_strdup (text));
+}
+
+
+
+void
+mousepad_replace_dialog_history_clean (void)
+{
+  GSList *li;
+
+  if (history_list)
+    {
+      /* remove all the entries */
+      for (li = history_list; li != NULL; li = li->next)
+        {
+          /* cleanup the string */
+          g_free (li->data);
+
+          /* remove the item from the list */
+          history_list = g_slist_delete_link (history_list, li);
+        }
+
+      /* cleanup the list */
+      g_slist_free (history_list);
+    }
+}
+
+
+
+GtkWidget *
+mousepad_replace_dialog_new (void)
+{
+  return g_object_new (MOUSEPAD_TYPE_REPLACE_DIALOG, NULL);
+}
diff --git a/mousepad/mousepad-replace-dialog.h b/mousepad/mousepad-replace-dialog.h
new file mode 100644
index 0000000..e66ad2d
--- /dev/null
+++ b/mousepad/mousepad-replace-dialog.h
@@ -0,0 +1,41 @@
+/* $Id$ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __MOUSEPAD_REPLACE_DIALOG_H__
+#define __MOUSEPAD_REPLACE_DIALOG_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_REPLACE_DIALOG            (mousepad_replace_dialog_get_type ())
+#define MOUSEPAD_REPLACE_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_REPLACE_DIALOG, MousepadReplaceDialog))
+#define MOUSEPAD_REPLACE_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_REPLACE_DIALOG, MousepadReplaceDialogClass))
+#define MOUSEPAD_IS_REPLACE_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_REPLACE_DIALOG))
+#define MOUSEPAD_IS_REPLACE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPADL_TYPE_REPLACE_DIALOG))
+#define MOUSEPAD_REPLACE_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_REPLACE_DIALOG, MousepadReplaceDialogClass))
+
+typedef struct _MousepadReplaceDialogClass MousepadReplaceDialogClass;
+typedef struct _MousepadReplaceDialog      MousepadReplaceDialog;
+
+GType           mousepad_replace_dialog_get_type       (void) G_GNUC_CONST;
+
+GtkWidget      *mousepad_replace_dialog_new            (void);
+
+void            mousepad_replace_dialog_history_clean  (void);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_REPLACE_DIALOG_H__ */
diff --git a/mousepad/mousepad-search-bar.c b/mousepad/mousepad-search-bar.c
index d770214..1850a30 100644
--- a/mousepad/mousepad-search-bar.c
+++ b/mousepad/mousepad-search-bar.c
@@ -24,54 +24,46 @@
 
 #include <mousepad/mousepad-private.h>
 #include <mousepad/mousepad-marshal.h>
-#include <mousepad/mousepad-types.h>
-#include <mousepad/mousepad-enum-types.h>
+#include <mousepad/mousepad-document.h>
 #include <mousepad/mousepad-search-bar.h>
 #include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-util.h>
 #include <mousepad/mousepad-window.h>
 
 
 
 #define TOOL_BAR_ICON_SIZE  GTK_ICON_SIZE_MENU
-#define HIGHTLIGHT_TIMEOUT  500
+#define HIGHTLIGHT_TIMEOUT  225
 
 
 
 static void      mousepad_search_bar_class_init                 (MousepadSearchBarClass  *klass);
-static void      mousepad_search_bar_init                       (MousepadSearchBar       *search_bar);
+static void      mousepad_search_bar_init                       (MousepadSearchBar       *bar);
 static void      mousepad_search_bar_finalize                   (GObject                 *object);
-static void      mousepad_search_bar_find_string                (MousepadSearchBar       *search_bar,
-                                                                 MousepadSearchFlags      flags);
-static void      mousepad_search_hide_clicked                   (MousepadSearchBar       *search_bar);
+static void      mousepad_search_bar_find_string                (MousepadSearchBar       *bar,
+                                                                 MousepadSearchFlags   flags);
+static void      mousepad_search_bar_hide_clicked               (MousepadSearchBar       *bar);
 static void      mousepad_search_bar_entry_changed              (GtkWidget               *entry,
-                                                                 MousepadSearchBar       *search_bar);
+                                                                 MousepadSearchBar       *bar);
 static void      mousepad_search_bar_highlight_toggled          (GtkWidget               *button,
-                                                                 MousepadSearchBar       *search_bar);
+                                                                 MousepadSearchBar       *bar);
 static void      mousepad_search_bar_match_case_toggled         (GtkWidget               *button,
-                                                                 MousepadSearchBar       *search_bar);
-static void      mousepad_search_bar_wrap_around_toggled        (GtkWidget               *button,
-                                                                 MousepadSearchBar       *search_bar);
-static void      mousepad_search_bar_match_whole_word_toggled   (GtkWidget               *button,
-                                                                 MousepadSearchBar       *search_bar);
+                                                                 MousepadSearchBar       *bar);
 static void      mousepad_search_bar_menuitem_toggled           (GtkCheckMenuItem        *item,
                                                                  GtkToggleButton         *button);
-static void      mousepad_search_bar_highlight_idle             (MousepadSearchBar       *search_bar,
-                                                                 gboolean                 forced);
+static void      mousepad_search_bar_highlight_schedule         (MousepadSearchBar       *bar);
 static gboolean  mousepad_search_bar_highlight_timeout          (gpointer                 user_data);
 static void      mousepad_search_bar_highlight_timeout_destroy  (gpointer                 user_data);
-static void      mousepad_search_bar_nothing_found              (MousepadSearchBar       *search_bar,
-                                                                 gboolean                 nothing_found);
+
 
 
 enum
 {
   HIDE_BAR,
-  FIND_STRING,
-  HIGHLIGHT_ALL,
+  SEARCH,
   LAST_SIGNAL,
 };
 
-
 struct _MousepadSearchBarClass
 {
   GtkToolbarClass __parent__;
@@ -81,6 +73,7 @@ struct _MousepadSearchBar
 {
   GtkToolbar           __parent__;
 
+  /* preferences */
   MousepadPreferences *preferences;
 
   /* text entry */
@@ -88,19 +81,12 @@ struct _MousepadSearchBar
 
   /* menu entries */
   GtkWidget           *match_case_entry;
-  GtkWidget           *wrap_around_entry;
-  GtkWidget           *match_whole_word_entry;
 
-  /* if something was found */
-  guint                nothing_found : 1;
-
-  /* settings */
+  /* flags */
   guint                highlight_all : 1;
   guint                match_case : 1;
-  guint                wrap_around : 1;
-  guint                match_whole_word : 1;
 
-  /* timeout for highlighting while typing */
+  /* highlight id */
   guint                highlight_id;
 };
 
@@ -158,28 +144,20 @@ mousepad_search_bar_class_init (MousepadSearchBarClass *klass)
   search_bar_signals[HIDE_BAR] =
     g_signal_new (I_("hide-bar"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
 
-  search_bar_signals[FIND_STRING] =
-    g_signal_new (I_("find-string"),
-                  G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  _mousepad_marshal_BOOLEAN__STRING_FLAGS,
-                  G_TYPE_BOOLEAN, 2,
-                  G_TYPE_STRING, MOUSEPAD_TYPE_SEARCH_FLAGS);
-
-  search_bar_signals[HIGHLIGHT_ALL] =
-    g_signal_new (I_("highlight-all"),
+  search_bar_signals[SEARCH] =
+    g_signal_new (I_("search"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS ,
                   0, NULL, NULL,
-                  _mousepad_marshal_BOOLEAN__STRING_FLAGS,
-                  G_TYPE_BOOLEAN, 2,
-                  G_TYPE_STRING, MOUSEPAD_TYPE_SEARCH_FLAGS);
+                  _mousepad_marshal_INT__FLAGS_STRING_STRING,
+                  G_TYPE_INT, 3,
+                  MOUSEPAD_TYPE_SEARCH_FLAGS,
+                  G_TYPE_STRING, G_TYPE_STRING);
 
   /* setup key bindings for the search bar */
   binding_set = gtk_binding_set_by_class (klass);
@@ -203,39 +181,31 @@ mousepad_search_bar_class_init (MousepadSearchBarClass *klass)
 
 
 static void
-mousepad_search_bar_init (MousepadSearchBar *search_bar)
+mousepad_search_bar_init (MousepadSearchBar *bar)
 {
   GtkWidget   *label, *image, *check, *menuitem;
   GtkToolItem *item;
-  gboolean     match_case, wrap_around, match_whole_word;
+  gboolean     match_case;
 
   /* preferences */
-  search_bar->preferences = mousepad_preferences_get ();
+  bar->preferences = mousepad_preferences_get ();
 
-  /* load some properties */
-  g_object_get (G_OBJECT (search_bar->preferences),
-                "last-match-case", &match_case,
-                "last-wrap-around", &wrap_around,
-                "last-match-whole-word", &match_whole_word,
-                NULL);
+  /* load some preferences */
+  g_object_get (G_OBJECT (bar->preferences), "search-match-case", &match_case, NULL);
 
   /* init variables */
-  search_bar->nothing_found = FALSE;
-  search_bar->highlight_id = 0;
-  search_bar->match_case = match_case;
-  search_bar->wrap_around = wrap_around;
-  search_bar->match_whole_word = match_whole_word;
+  bar->highlight_id = 0;
+  bar->match_case = match_case;
 
   /* the close button */
   item = gtk_tool_button_new_from_stock (GTK_STOCK_CLOSE);
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+  g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_hide_clicked), bar);
   gtk_widget_show (GTK_WIDGET (item));
-  g_signal_connect_swapped (G_OBJECT (item), "clicked",
-                            G_CALLBACK (mousepad_search_hide_clicked), search_bar);
 
   /* the find label */
   item = gtk_tool_item_new ();
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
   gtk_widget_show (GTK_WIDGET (item));
 
   label = gtk_label_new_with_mnemonic (_("Fi_nd:"));
@@ -245,15 +215,14 @@ mousepad_search_bar_init (MousepadSearchBar *search_bar)
 
   /* the entry field */
   item = gtk_tool_item_new ();
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
   gtk_widget_show (GTK_WIDGET (item));
 
-  search_bar->entry = gtk_entry_new ();
-  gtk_container_add (GTK_CONTAINER (item), search_bar->entry);
-  gtk_label_set_mnemonic_widget (GTK_LABEL (label), search_bar->entry);
-  gtk_widget_show (search_bar->entry);
-  g_signal_connect (G_OBJECT (search_bar->entry), "changed",
-                    G_CALLBACK (mousepad_search_bar_entry_changed), search_bar);
+  bar->entry = gtk_entry_new ();
+  gtk_container_add (GTK_CONTAINER (item), bar->entry);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), bar->entry);
+  g_signal_connect (G_OBJECT (bar->entry), "changed", G_CALLBACK (mousepad_search_bar_entry_changed), bar);
+  gtk_widget_show (bar->entry);
 
   /* next button */
   image = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, TOOL_BAR_ICON_SIZE);
@@ -262,10 +231,9 @@ mousepad_search_bar_init (MousepadSearchBar *search_bar)
   item = gtk_tool_button_new (image, _("_Next"));
   gtk_tool_item_set_is_important (item, TRUE);
   gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (item), TRUE);
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+  g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_find_next), bar);
   gtk_widget_show (GTK_WIDGET (item));
-  g_signal_connect_swapped (G_OBJECT (item), "clicked",
-                            G_CALLBACK (mousepad_search_bar_find_next), search_bar);
 
   /* previous button */
   image = gtk_image_new_from_stock (GTK_STOCK_GO_UP, TOOL_BAR_ICON_SIZE);
@@ -274,88 +242,40 @@ mousepad_search_bar_init (MousepadSearchBar *search_bar)
   item = gtk_tool_button_new (image, _("_Previous"));
   gtk_tool_item_set_is_important (item, TRUE);
   gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (item), TRUE);
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+  g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_find_previous), bar);
   gtk_widget_show (GTK_WIDGET (item));
-  g_signal_connect_swapped (G_OBJECT (item), "clicked",
-                            G_CALLBACK (mousepad_search_bar_find_previous), search_bar);
 
   /* highlight all */
   item = (GtkToolItem *) gtk_toggle_tool_button_new ();
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+  g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
   gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), GTK_STOCK_SELECT_ALL);
   gtk_tool_button_set_label (GTK_TOOL_BUTTON (item), _("Highlight _All"));
   gtk_tool_item_set_is_important (item, TRUE);
   gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (item), TRUE);
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+  g_signal_connect (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_highlight_toggled), bar);
   gtk_widget_show (GTK_WIDGET (item));
-  g_signal_connect (G_OBJECT (item), "clicked",
-                    G_CALLBACK (mousepad_search_bar_highlight_toggled), search_bar);
 
   /* check button for case sensitive, including the proxy menu item */
   item = gtk_tool_item_new ();
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
+  g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+  gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
   gtk_widget_show (GTK_WIDGET (item));
 
   check = gtk_check_button_new_with_mnemonic (_("Mat_ch Case"));
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+  g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
   gtk_container_add (GTK_CONTAINER (item), check);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), match_case);
+  g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_search_bar_match_case_toggled), bar);
   gtk_widget_show (check);
-  g_signal_connect (G_OBJECT (check), "toggled",
-                    G_CALLBACK (mousepad_search_bar_match_case_toggled), search_bar);
 
-  search_bar->match_case_entry = menuitem = gtk_check_menu_item_new_with_mnemonic (_("Mat_ch Case"));
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+  bar->match_case_entry = menuitem = gtk_check_menu_item_new_with_mnemonic (_("Mat_ch Case"));
+  g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
   gtk_tool_item_set_proxy_menu_item (item, "case-sensitive", menuitem);
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), match_case);
+  g_signal_connect (G_OBJECT (menuitem), "toggled", G_CALLBACK (mousepad_search_bar_menuitem_toggled), check);
   gtk_widget_show (menuitem);
-  g_signal_connect (G_OBJECT (menuitem), "toggled",
-                    G_CALLBACK (mousepad_search_bar_menuitem_toggled), check);
-
-  /* check button for wrap around, including the proxy menu item */
-  item = gtk_tool_item_new ();
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
-  gtk_widget_show (GTK_WIDGET (item));
-
-  check = gtk_check_button_new_with_mnemonic (_("W_rap Around"));
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
-  gtk_container_add (GTK_CONTAINER (item), check);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), wrap_around);
-  gtk_widget_show (check);
-  g_signal_connect (G_OBJECT (check), "toggled",
-                    G_CALLBACK (mousepad_search_bar_wrap_around_toggled), search_bar);
-
-  search_bar->wrap_around_entry = menuitem = gtk_check_menu_item_new_with_mnemonic (_("W_rap Around"));
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
-  gtk_tool_item_set_proxy_menu_item (item, "case-sensitive", menuitem);
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), wrap_around);
-  gtk_widget_show (menuitem);
-  g_signal_connect (G_OBJECT (menuitem), "toggled",
-                    G_CALLBACK (mousepad_search_bar_menuitem_toggled), check);
-
-  /* check button for match whole word, including the proxy menu item */
-  item = gtk_tool_item_new ();
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
-  gtk_toolbar_insert (GTK_TOOLBAR (search_bar), item, -1);
-  gtk_widget_show (GTK_WIDGET (item));
-
-  check = gtk_check_button_new_with_mnemonic (_("Match _Whole Word"));
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
-  gtk_container_add (GTK_CONTAINER (item), check);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), match_whole_word);
-  gtk_widget_show (check);
-  g_signal_connect (G_OBJECT (check), "toggled",
-                    G_CALLBACK (mousepad_search_bar_match_whole_word_toggled), search_bar);
-
-  search_bar->match_whole_word_entry = menuitem = gtk_check_menu_item_new_with_mnemonic (_("Match _Whole Word"));
-  g_signal_connect_object (G_OBJECT (search_bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
-  gtk_tool_item_set_proxy_menu_item (item, "case-sensitive", menuitem);
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), match_whole_word);
-  gtk_widget_show (menuitem);
-  g_signal_connect (G_OBJECT (menuitem), "toggled",
-                    G_CALLBACK (mousepad_search_bar_menuitem_toggled), check);
 }
 
 
@@ -363,14 +283,14 @@ mousepad_search_bar_init (MousepadSearchBar *search_bar)
 static void
 mousepad_search_bar_finalize (GObject *object)
 {
-  MousepadSearchBar *search_bar = MOUSEPAD_SEARCH_BAR (object);
+  MousepadSearchBar *bar = MOUSEPAD_SEARCH_BAR (object);
 
   /* release the preferences */
-  g_object_unref (G_OBJECT (search_bar->preferences));
+  g_object_unref (G_OBJECT (bar->preferences));
 
   /* stop a running highlight timeout */
-  if (search_bar->highlight_id != 0)
-    g_source_remove (search_bar->highlight_id);
+  if (bar->highlight_id != 0)
+    g_source_remove (bar->highlight_id);
 
   (*G_OBJECT_CLASS (mousepad_search_bar_parent_class)->finalize) (object);
 }
@@ -378,190 +298,152 @@ mousepad_search_bar_finalize (GObject *object)
 
 
 static void
-mousepad_search_bar_find_string (MousepadSearchBar *search_bar,
-                                 MousepadSearchFlags flags)
+mousepad_search_bar_find_string (MousepadSearchBar   *bar,
+                                 MousepadSearchFlags  flags)
 {
   const gchar *string;
-  gboolean     result;
+  gint         nmatches;
 
-  /* append the insensitive flags when needed */
-  if (!search_bar->match_case)
-    flags |= MOUSEPAD_SEARCH_CASE_INSENSITIVE;
+  /* search the entire document */
+  flags |= MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT;
 
-  /* wrap around flag */
-  if (search_bar->wrap_around)
-    flags |= MOUSEPAD_SEARCH_WRAP_AROUND;
+  /* if we don't hightlight, we select with wrapping */
+  if ((flags & MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT) == 0)
+    flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT
+             | MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND;
 
-  /* wrap around flag */
-  if (search_bar->match_whole_word)
-    flags |= MOUSEPAD_SEARCH_WHOLE_WORD;
+  /* append the insensitive flags when needed */
+  if (bar->match_case)
+    flags |= MOUSEPAD_SEARCH_FLAGS_MATCH_CASE;
 
   /* get the entry string */
-  string = gtk_entry_get_text (GTK_ENTRY (search_bar->entry));
+  string = gtk_entry_get_text (GTK_ENTRY (bar->entry));
 
-  /* send the signal and wait for the result */
-  g_signal_emit (G_OBJECT (search_bar), search_bar_signals[FIND_STRING], 0,
-                 string, flags, &result);
+  /* emit signal */
+  g_signal_emit (G_OBJECT (bar), search_bar_signals[SEARCH], 0, flags, string, NULL, &nmatches);
 
   /* make sure the search entry is not red when no text was typed */
   if (string == NULL || *string == '\0')
-    result = TRUE;
+    nmatches = 1;
 
   /* change the entry style */
-  mousepad_search_bar_nothing_found (search_bar, !result);
+  mousepad_util_entry_error (bar->entry, nmatches < 1);
 }
 
 
 
 static void
-mousepad_search_hide_clicked (MousepadSearchBar *search_bar)
+mousepad_search_bar_hide_clicked (MousepadSearchBar *bar)
 {
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
 
   /* emit the signal */
-  g_signal_emit (G_OBJECT (search_bar), search_bar_signals[HIDE_BAR], 0);
+  g_signal_emit (G_OBJECT (bar), search_bar_signals[HIDE_BAR], 0);
 }
 
 
 
 static void
 mousepad_search_bar_entry_changed (GtkWidget         *entry,
-                                   MousepadSearchBar *search_bar)
+                                   MousepadSearchBar *bar)
 {
-  /* stop a running highlight timeout */
-  if (search_bar->highlight_id != 0)
-    g_source_remove (search_bar->highlight_id);
+  MousepadSearchFlags flags;
 
-  /* re-run the highlight */
-  mousepad_search_bar_highlight_idle (search_bar, FALSE);
+  /* set the search flags */
+  flags = MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START
+          | MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD;
 
   /* find */
-  mousepad_search_bar_find_string (search_bar, 0);
+  mousepad_search_bar_find_string (bar, flags);
+
+  /* schedule a new highlight */
+  if (bar->highlight_all)
+    mousepad_search_bar_highlight_schedule (bar);
 }
 
 
 
 static void
 mousepad_search_bar_highlight_toggled (GtkWidget         *button,
-                                       MousepadSearchBar *search_bar)
+                                       MousepadSearchBar *bar)
 {
-  gboolean active;
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
 
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
+  /* set the new state */
+  bar->highlight_all = gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button));
 
-  /* get the state of the toggle button */
-  active = gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button));
-
-  /* stop a running highlight timeout if the button is inactive*/
-  if (search_bar->highlight_id != 0 && !active)
-    g_source_remove (search_bar->highlight_id);
-
-  /* save the state */
-  search_bar->highlight_all = active;
-
-  /* re-run the highlight */
-  mousepad_search_bar_highlight_idle (search_bar, TRUE);
+  /* reschedule the highlight */
+  mousepad_search_bar_highlight_schedule (bar);
 }
 
 
 
 static void
 mousepad_search_bar_match_case_toggled (GtkWidget         *button,
-                                        MousepadSearchBar *search_bar)
+                                        MousepadSearchBar *bar)
 {
   gboolean active;
 
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
 
   /* get the state of the toggle button */
   active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
 
   /* set the state of the menu item */
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (search_bar->match_case_entry), active);
+  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (bar->match_case_entry), active);
 
   /* save the state */
-  search_bar->match_case = active;
+  bar->match_case = active;
 
   /* save the setting */
-  g_object_set (G_OBJECT (search_bar->preferences), "last-match-case", active, NULL);
-
-  /* re-run the highlight */
-  mousepad_search_bar_highlight_idle (search_bar, FALSE);
-}
-
-
-
-static void
-mousepad_search_bar_wrap_around_toggled (GtkWidget         *button,
-                                         MousepadSearchBar *search_bar)
-{
-  gboolean active;
-
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
-
-  /* get the state of the toggle button */
-  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+  g_object_set (G_OBJECT (bar->preferences), "search-match-case", active, NULL);
 
-  /* set the state of the menu item */
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (search_bar->wrap_around_entry), active);
+  /* search ahead with this new flags */
+  mousepad_search_bar_entry_changed (NULL, bar);
 
-  /* save the state */
-  search_bar->wrap_around = active;
-
-  /* save the setting */
-  g_object_set (G_OBJECT (search_bar->preferences), "last-wrap-around", active, NULL);
+  /* schedule a new hightlight */
+  if (bar->highlight_all)
+    mousepad_search_bar_highlight_schedule (bar);
 }
 
 
 
 static void
-mousepad_search_bar_match_whole_word_toggled (GtkWidget         *button,
-                                              MousepadSearchBar *search_bar)
+mousepad_search_bar_menuitem_toggled (GtkCheckMenuItem *item,
+                                      GtkToggleButton  *button)
 {
   gboolean active;
 
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
-
-  /* get the state of the toggle button */
-  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
-
-  /* set the state of the menu item */
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (search_bar->match_whole_word_entry), active);
-
-  /* save the state */
-  search_bar->match_whole_word = active;
-
-  /* save the setting */
-  g_object_set (G_OBJECT (search_bar->preferences), "last-match-whole-word", active, NULL);
+  _mousepad_return_if_fail (GTK_IS_CHECK_MENU_ITEM (item));
+  _mousepad_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
 
-  /* re-run the highlight */
-  mousepad_search_bar_highlight_idle (search_bar, FALSE);
+  /* toggle the menubar item, he/she will send the signal */
+  active = gtk_check_menu_item_get_active (item);
+  gtk_toggle_button_set_active (button, active);
 }
 
 
 
 static void
-mousepad_search_bar_menuitem_toggled (GtkCheckMenuItem *item,
-                                      GtkToggleButton  *button)
+mousepad_search_bar_highlight_schedule (MousepadSearchBar *bar)
 {
-  gboolean active;
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
 
-  active = gtk_check_menu_item_get_active (item);
-  gtk_toggle_button_set_active (button, active);
+  /* stop a pending timeout */
+  if (bar->highlight_id != 0)
+    g_source_remove (bar->highlight_id);
+
+  /* schedule a new timeout */
+  bar->highlight_id = g_timeout_add_full (G_PRIORITY_LOW, HIGHTLIGHT_TIMEOUT, mousepad_search_bar_highlight_timeout,
+                                          bar, mousepad_search_bar_highlight_timeout_destroy);
 }
 
 
 
 static void
-mousepad_search_bar_highlight_idle (MousepadSearchBar *search_bar,
-                                    gboolean           forced)
+mousepad_search_bar_highlight_timeout_destroy (gpointer user_data)
 {
-  if ((forced || search_bar->highlight_all) && search_bar->highlight_id == 0)
-    {
-      /* invoke the highlight function to update the buffer */
-      search_bar->highlight_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_search_bar_highlight_timeout,
-                                                  search_bar, mousepad_search_bar_highlight_timeout_destroy);
-    }
+  MOUSEPAD_SEARCH_BAR (user_data)->highlight_id = 0;
 }
 
 
@@ -569,30 +451,22 @@ mousepad_search_bar_highlight_idle (MousepadSearchBar *search_bar,
 static gboolean
 mousepad_search_bar_highlight_timeout (gpointer user_data)
 {
-  MousepadSearchBar   *search_bar = MOUSEPAD_SEARCH_BAR (user_data);
-  const gchar         *string = NULL;
-  gboolean             dummy;
-  MousepadSearchFlags  flags = 0;
+  MousepadSearchBar   *bar = MOUSEPAD_SEARCH_BAR (user_data);
+  MousepadSearchFlags  flags;
 
   GDK_THREADS_ENTER ();
 
-  /* append the insensitive case flag if needed */
-  if (!search_bar->match_case)
-    flags |= MOUSEPAD_SEARCH_CASE_INSENSITIVE;
-
-  /* wrap around flag */
-  if (search_bar->match_whole_word)
-    flags |= MOUSEPAD_SEARCH_WHOLE_WORD;
+  /* set search flags */
+  flags = MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD
+          | MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START
+          | MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT;
 
-  /* set the entry string or a 0 string to remove the highlight */
-  if (search_bar->highlight_all)
-    string = gtk_entry_get_text (GTK_ENTRY (search_bar->entry));
-  else
-    string = "\0";
+  /* only clear when there is no text */
+  if (!bar->highlight_all)
+    flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_CLEANUP;
 
-  /* send the signal and wait for the result */
-  g_signal_emit (G_OBJECT (search_bar), search_bar_signals[HIGHLIGHT_ALL], 0,
-                 string, flags, &dummy);
+  /* hightlight */
+  mousepad_search_bar_find_string (bar, flags);
 
   GDK_THREADS_LEAVE ();
 
@@ -602,63 +476,60 @@ mousepad_search_bar_highlight_timeout (gpointer user_data)
 
 
 
-static void
-mousepad_search_bar_highlight_timeout_destroy (gpointer user_data)
+GtkEditable *
+mousepad_search_bar_entry (MousepadSearchBar *bar)
 {
-  MOUSEPAD_SEARCH_BAR (user_data)->highlight_id = 0;
+  if (bar && GTK_WIDGET_HAS_FOCUS (bar->entry))
+    return GTK_EDITABLE (bar->entry);
+  else
+    return NULL;
 }
 
 
 
-static void
-mousepad_search_bar_nothing_found (MousepadSearchBar *search_bar,
-                                   gboolean           nothing_found)
+void
+mousepad_search_bar_focus (MousepadSearchBar *bar)
 {
-  const GdkColor red   = {0, 0xffff, 0x6666, 0x6666};
-  const GdkColor white = {0, 0xffff, 0xffff, 0xffff};
-
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
 
-  /* only update the style if really needed */
-  if (search_bar->nothing_found != nothing_found)
-    {
-      /* (re)set the base and font color */
-      gtk_widget_modify_base (search_bar->entry, GTK_STATE_NORMAL, nothing_found ? &red : NULL);
-      gtk_widget_modify_text (search_bar->entry, GTK_STATE_NORMAL, nothing_found ? &white : NULL);
+  /* focus the entry field */
+  gtk_widget_grab_focus (bar->entry);
 
-      search_bar->nothing_found = nothing_found;
-    }
+  /* update the highlight */
+  if (bar->highlight_all)
+    mousepad_search_bar_highlight_schedule (bar);
 }
 
 
 
 void
-mousepad_search_bar_focus (MousepadSearchBar *search_bar)
+mousepad_search_bar_find_next (MousepadSearchBar *bar)
 {
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
-
-  /* focus the entry field */
-  gtk_widget_grab_focus (search_bar->entry);
-}
+  MousepadSearchFlags flags;
 
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
 
+  /* set search flags */
+  flags = MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END
+          | MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD;
 
-void
-mousepad_search_bar_find_next (MousepadSearchBar *search_bar)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
-
-  /* find */
-  mousepad_search_bar_find_string (search_bar, MOUSEPAD_SEARCH_FORWARDS);
+  /* search */
+  mousepad_search_bar_find_string (bar, flags);
 }
 
 
 
 void
-mousepad_search_bar_find_previous (MousepadSearchBar *search_bar)
+mousepad_search_bar_find_previous (MousepadSearchBar *bar)
 {
-  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (search_bar));
+  MousepadSearchFlags flags;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+  /* set search flags */
+  flags = MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START
+          | MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD;
 
-  /* find backwards */
-  mousepad_search_bar_find_string (search_bar, MOUSEPAD_SEARCH_BACKWARDS);
+  /* search */
+  mousepad_search_bar_find_string (bar, flags);
 }
diff --git a/mousepad/mousepad-search-bar.h b/mousepad/mousepad-search-bar.h
index 8054aae..8d80096 100644
--- a/mousepad/mousepad-search-bar.h
+++ b/mousepad/mousepad-search-bar.h
@@ -30,15 +30,17 @@ G_BEGIN_DECLS
 typedef struct _MousepadSearchBarClass MousepadSearchBarClass;
 typedef struct _MousepadSearchBar      MousepadSearchBar;
 
-GType           mousepad_search_bar_get_type       (void) G_GNUC_CONST;
+GType           mousepad_search_bar_get_type        (void) G_GNUC_CONST;
 
-GtkWidget      *mousepad_search_bar_new            (void);
+GtkWidget      *mousepad_search_bar_new             (void);
 
-void            mousepad_search_bar_focus          (MousepadSearchBar *search_bar);
+GtkEditable    *mousepad_search_bar_entry           (MousepadSearchBar *bar);
 
-void            mousepad_search_bar_find_next      (MousepadSearchBar *search_bar);
+void            mousepad_search_bar_focus           (MousepadSearchBar *bar);
 
-void            mousepad_search_bar_find_previous  (MousepadSearchBar *search_bar);
+void            mousepad_search_bar_find_next       (MousepadSearchBar *bar);
+
+void            mousepad_search_bar_find_previous   (MousepadSearchBar *bar);
 
 G_END_DECLS
 
diff --git a/mousepad/mousepad-statusbar.c b/mousepad/mousepad-statusbar.c
index 0ddd6da..81e851d 100644
--- a/mousepad/mousepad-statusbar.c
+++ b/mousepad/mousepad-statusbar.c
@@ -22,6 +22,7 @@
 #include <mousepad/mousepad-private.h>
 #include <mousepad/mousepad-statusbar.h>
 #include <mousepad/mousepad-window.h>
+#include <mousepad/mousepad-util.h>
 
 
 
@@ -101,17 +102,10 @@ mousepad_statusbar_class_init (MousepadStatusbarClass *klass)
   statusbar_signals[ENABLE_OVERWRITE] =
     g_signal_new (I_("enable-overwrite"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__BOOLEAN,
                   G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
-  /* don't show the statusbar frame */
-  gtk_rc_parse_string ("style \"mousepad-statusbar-style\"\n"
-                       "  {\n"
-                       "    GtkStatusbar::shadow-type = GTK_SHADOW_NONE\n"
-                       "  }\n"
-                       "class \"MousepadStatusbar\" style \"mousepad-statusbar-style\"\n");
 }
 
 
@@ -119,26 +113,40 @@ mousepad_statusbar_class_init (MousepadStatusbarClass *klass)
 static void
 mousepad_statusbar_init (MousepadStatusbar *statusbar)
 {
-  GtkWidget *ebox;
+  GtkWidget    *ebox, *box;
+  GtkStatusbar *bar = GTK_STATUSBAR (statusbar);
+
+  /* init statusbar */
+  gtk_statusbar_set_has_resize_grip (bar, TRUE);
 
-  /* set statusbar spaceing */
-  gtk_box_set_spacing (GTK_BOX (statusbar), 8);
+  /* create a new horizontal box */
+  box = gtk_hbox_new (FALSE, 8);
+  gtk_widget_show (box);
+
+  /* reorder the gtk statusbar */
+  g_object_ref (G_OBJECT (bar->label));
+  gtk_container_remove (GTK_CONTAINER (bar->frame), bar->label);
+  gtk_container_add (GTK_CONTAINER (bar->frame), box);
+  gtk_box_pack_start (GTK_BOX (box), bar->label, TRUE, TRUE, 0);
+  g_object_unref (G_OBJECT (bar->label));
 
   /* line and column numbers */
   statusbar->position = gtk_label_new (NULL);
-  gtk_box_pack_start (GTK_BOX (statusbar), statusbar->position, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), statusbar->position, FALSE, TRUE, 0);
+  gtk_widget_show (statusbar->position);
 
-  /* overwrite */
+  /* overwrite event box */
   ebox = gtk_event_box_new ();
-  gtk_box_pack_start (GTK_BOX (statusbar), ebox, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, TRUE, 0);
   gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE);
+  mousepad_util_set_tooltip (ebox, _("Toggle the overwrite mode"));
+  g_signal_connect (G_OBJECT (ebox), "button-press-event", G_CALLBACK (mousepad_statusbar_overwrite_clicked), statusbar);
   gtk_widget_show (ebox);
-  mousepad_gtk_set_tooltip (ebox, _("Toggle the overwrite mode"));
-  g_signal_connect (G_OBJECT (ebox), "button-press-event",
-                    G_CALLBACK (mousepad_statusbar_overwrite_clicked), statusbar);
 
+  /* overwrite label */
   statusbar->overwrite = gtk_label_new (_("OVR"));
   gtk_container_add (GTK_CONTAINER (ebox), statusbar->overwrite);
+  gtk_widget_show (statusbar->overwrite);
 }
 
 
@@ -170,13 +178,15 @@ mousepad_statusbar_set_cursor_position (MousepadStatusbar *statusbar,
                                         gint               line,
                                         gint               column)
 {
-  gchar *string;
+  gchar string[64];
 
   _mousepad_return_if_fail (MOUSEPAD_IS_STATUSBAR (statusbar));
 
-  string = g_strdup_printf (_("Line %d Col %d"), line, column);
+  /* create printable string */
+  g_snprintf (string, sizeof (string), _("Line %d Col %d"), line, column);
+
+  /* set label */
   gtk_label_set_text (GTK_LABEL (statusbar->position), string);
-  g_free (string);
 }
 
 
@@ -191,23 +201,3 @@ mousepad_statusbar_set_overwrite (MousepadStatusbar *statusbar,
 
   statusbar->overwrite_enabled = overwrite;
 }
-
-
-
-void
-mousepad_statusbar_visible (MousepadStatusbar *statusbar,
-                            gboolean           visible)
-{
-  _mousepad_return_if_fail (MOUSEPAD_IS_STATUSBAR (statusbar));
-
-  if (visible)
-    {
-      gtk_widget_show (statusbar->position);
-      gtk_widget_show (statusbar->overwrite);
-    }
-  else
-    {
-      gtk_widget_hide (statusbar->position);
-      gtk_widget_hide (statusbar->overwrite);
-    }
-}
diff --git a/mousepad/mousepad-statusbar.h b/mousepad/mousepad-statusbar.h
index cdbeaf1..bd5b265 100644
--- a/mousepad/mousepad-statusbar.h
+++ b/mousepad/mousepad-statusbar.h
@@ -41,9 +41,6 @@ void        mousepad_statusbar_set_cursor_position  (MousepadStatusbar *statusba
 void        mousepad_statusbar_set_overwrite        (MousepadStatusbar *statusbar,
                                                      gboolean           overwrite);
 
-void        mousepad_statusbar_visible              (MousepadStatusbar *statusbar,
-                                                     gboolean           visible);
-
 G_END_DECLS
 
 #endif /* !__MOUSEPAD_STATUSBAR_H__ */
diff --git a/mousepad/mousepad-types.h b/mousepad/mousepad-types.h
deleted file mode 100644
index 23c8e53..0000000
--- a/mousepad/mousepad-types.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* $Id$ */
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef __MOUSEPAD_TYPES_H__
-#define __MOUSEPAD_TYPES_H__
-
-G_BEGIN_DECLS
-
-typedef enum
-{
-  MOUSEPAD_SEARCH_CASE_INSENSITIVE = 1 << 1,
-  MOUSEPAD_SEARCH_WRAP_AROUND      = 1 << 2,
-  MOUSEPAD_SEARCH_FORWARDS         = 1 << 3,
-  MOUSEPAD_SEARCH_BACKWARDS        = 1 << 4,
-  MOUSEPAD_SEARCH_WHOLE_WORD       = 1 << 5,
-} MousepadSearchFlags;
-
-G_END_DECLS
-
-#endif /* !__MOUSEPAD_TYPES_H__ */
diff --git a/mousepad/mousepad-undo.c b/mousepad/mousepad-undo.c
index dde4863..284f7a3 100644
--- a/mousepad/mousepad-undo.c
+++ b/mousepad/mousepad-undo.c
@@ -23,15 +23,43 @@
 #include <mousepad/mousepad-undo.h>
 
 
+#define DEFAULT_CACHE_SIZE (50)
 
-/* maximum number of steps in the undo manager */
-#define MAX_UNDO_STEPS 75
 
 
-
-typedef struct _MousepadUndoInfo   MousepadUndoInfo;
+typedef struct _MousepadUndoCache  MousepadUndoCache;
+typedef struct _MousepadUndoStep   MousepadUndoStep;
 typedef enum   _MousepadUndoAction MousepadUndoAction;
 
+
+
+static void mousepad_undo_class_init         (MousepadUndoClass  *klass);
+static void mousepad_undo_init               (MousepadUndo       *undo);
+static void mousepad_undo_finalize           (GObject            *object);
+static void mousepad_undo_emit_signals       (MousepadUndo       *undo);
+static void mousepad_undo_step_free          (MousepadUndoStep   *step,
+                                              MousepadUndo       *undo);
+static void mousepad_undo_step_perform       (MousepadUndo       *undo,
+                                              gboolean            redo);
+static void mousepad_undo_cache_to_step      (MousepadUndo       *undo);
+static void mousepad_undo_cache_reset_needle (MousepadUndo       *undo);
+static void mousepad_undo_cache_update       (MousepadUndo       *undo,
+                                              MousepadUndoAction  action,
+                                              gint                start,
+                                              gint                end,
+                                              const gchar        *text);
+static void mousepad_undo_buffer_insert      (GtkTextBuffer      *buffer,
+                                              GtkTextIter        *pos,
+                                              const gchar        *text,
+                                              gint                length,
+                                              MousepadUndo       *undo);
+static void mousepad_undo_buffer_delete      (GtkTextBuffer      *buffer,
+                                              GtkTextIter        *start_iter,
+                                              GtkTextIter        *end_iter,
+                                              MousepadUndo       *undo);
+
+
+
 enum
 {
   CAN_UNDO,
@@ -39,15 +67,34 @@ enum
   LAST_SIGNAL
 };
 
+struct _MousepadUndoClass
+{
+  GObjectClass __parent__;
+};
+
 enum _MousepadUndoAction
 {
-  INSERT,
-  DELETE,
+  INSERT,  /* insert action */
+  DELETE,  /* delete action */
 };
 
-struct _MousepadUndoClass
+struct _MousepadUndoCache
 {
-  GObjectClass __parent__;
+  /* string to cache inserted or deleted character */
+  GString            *string;
+
+  /* current cached action */
+  MousepadUndoAction  action;
+
+  /* cache start and end positions */
+  gint                start;
+  gint                end;
+
+  /* whether the last character was a word breaking character */
+  guint               is_space : 1;
+
+  /* whether the changes in the cache are part of a group */
+  guint               in_group : 1;
 };
 
 struct _MousepadUndo
@@ -55,63 +102,43 @@ struct _MousepadUndo
   GObject __parent__;
 
   /* the text buffer we're monitoring */
-  GtkTextBuffer      *buffer;
+  GtkTextBuffer     *buffer;
 
-  /* whether the undo monitor is locked */
-  guint               locked;
+  /* whether the undo manager is locked */
+  gint               locked;
 
-  /* list of undo steps */
-  GList              *steps;
+  /* whether we should put multiple changes into one action */
+  gint               grouping;
 
-  /* position in the steps list */
-  gint                steps_position;
+  /* whether we can undo or redo */
+  guint              can_undo : 1;
+  guint              can_redo : 1;
 
-  /* the buffer of the active step */
-  GString            *step_buffer;
+  /* list containing the steps */
+  GList             *steps;
 
-  /* the info of the active step */
-  MousepadUndoAction  step_action;
-  gint                step_start;
-  gint                step_end;
-  guint               step_isspaces : 1;
+  /* steps list pointer when undoing */
+  GList             *needle;
 
-  /* whether we can undo and redo */
-  guint               can_undo : 1;
-  guint               can_redo : 1;
+  /* internal cache */
+  MousepadUndoCache  cache;
 };
 
-struct _MousepadUndoInfo
+struct _MousepadUndoStep
 {
-  /* the action of the undo step */
+  /* step action */
   MousepadUndoAction  action;
 
-  /* the deleted or inserted string */
-  gchar              *string;
+  /* pointer to the string or another entry in the list */
+  gpointer            data;
 
-  /* the start and end position in the buffer */
+  /* start and end positions */
   gint                start;
   gint                end;
-};
-
 
-
-static void  mousepad_undo_class_init       (MousepadUndoClass  *klass);
-static void  mousepad_undo_init             (MousepadUndo       *undo);
-static void  mousepad_undo_finalize         (GObject            *object);
-static void  mousepad_undo_free_step        (MousepadUndoInfo   *info,
-                                             MousepadUndo       *undo);
-static void  mousepad_undo_preform_step     (MousepadUndo       *undo,
-                                             gboolean            undo_step);
-static void  mousepad_undo_new_step         (MousepadUndo       *undo);
-static void  mousepad_undo_insert_text      (GtkTextBuffer      *buffer,
-                                             GtkTextIter        *pos,
-                                             const gchar        *text,
-                                             gint                length,
-                                             MousepadUndo       *undo);
-static void  mousepad_undo_delete_range     (GtkTextBuffer      *buffer,
-                                             GtkTextIter        *start_iter,
-                                             GtkTextIter        *end_iter,
-                                             MousepadUndo       *undo);
+  /* whether this step is part of a group */
+  guint               in_group : 1;
+};
 
 
 
@@ -154,7 +181,7 @@ mousepad_undo_class_init (MousepadUndoClass *klass)
   undo_signals[CAN_UNDO] =
     g_signal_new (I_("can-undo"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__BOOLEAN,
                   G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
@@ -162,7 +189,7 @@ mousepad_undo_class_init (MousepadUndoClass *klass)
   undo_signals[CAN_REDO] =
     g_signal_new (I_("can-redo"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__BOOLEAN,
                   G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
@@ -173,17 +200,20 @@ mousepad_undo_class_init (MousepadUndoClass *klass)
 static void
 mousepad_undo_init (MousepadUndo *undo)
 {
-  /* reset some variabled */
+  /* initialize */
   undo->locked = 0;
-  undo->steps = NULL;
-  undo->steps_position = 0;
-
-  /* we can't undo or redo */
+  undo->grouping = 0;
   undo->can_undo = FALSE;
   undo->can_redo = FALSE;
-
-  /* allocate the string buffer (we prealloc 15 characters to avoid multiple reallocations) */
-  undo->step_buffer = g_string_sized_new (15);
+  undo->steps = NULL;
+  undo->needle = NULL;
+
+  /* initialize the cache */
+  undo->cache.string = NULL;
+  undo->cache.start = -1;
+  undo->cache.end = -1;
+  undo->cache.is_space = FALSE;
+  undo->cache.in_group = FALSE;
 }
 
 
@@ -192,17 +222,12 @@ static void
 mousepad_undo_finalize (GObject *object)
 {
   MousepadUndo *undo = MOUSEPAD_UNDO (object);
-  GList        *li;
 
-  /* cleanup the undo steps */
-  for (li = undo->steps; li != NULL; li = li->next)
-    mousepad_undo_free_step (li->data, undo);
-
-  /* free the list */
-  g_list_free (undo->steps);
+  /* lock to avoid updates */
+  mousepad_undo_lock (undo);
 
-  /* cleanup the monitor step */
-  g_string_free (undo->step_buffer, TRUE);
+  /* clear the undo manager */
+  mousepad_undo_clear (undo);
 
   /* release the undo reference from the buffer */
   g_object_unref (G_OBJECT (undo->buffer));
@@ -213,286 +238,316 @@ mousepad_undo_finalize (GObject *object)
 
 
 static void
-mousepad_undo_free_step (MousepadUndoInfo *info,
+mousepad_undo_emit_signals (MousepadUndo *undo)
+{
+  gboolean can_undo, can_redo;
+
+  /* detect if we can undo or redo */
+  can_undo = (undo->needle != NULL);
+  can_redo = (undo->needle == NULL || g_list_previous (undo->needle) != NULL);
+
+  /* emit signals if needed */
+  if (undo->can_undo != can_undo)
+    {
+      undo->can_undo = can_undo;
+      g_signal_emit (G_OBJECT (undo), undo_signals[CAN_UNDO], 0, can_undo);
+    }
+
+  if (undo->can_redo != can_redo)
+    {
+      undo->can_redo = can_redo;
+      g_signal_emit (G_OBJECT (undo), undo_signals[CAN_REDO], 0, can_redo);
+    }
+}
+
+
+
+static void
+mousepad_undo_step_free (MousepadUndoStep *step,
                          MousepadUndo     *undo)
 {
   /* remove from the list */
-  undo->steps = g_list_remove (undo->steps, info);
+  if (G_LIKELY (undo))
+    undo->steps = g_list_remove (undo->steps, step);
 
   /* free the string */
-  g_free (info->string);
+  g_free (step->data);
 
   /* free the slice */
-  g_slice_free (MousepadUndoInfo, info);
+  g_slice_free (MousepadUndoStep, step);
 }
 
 
 
 static void
-mousepad_undo_preform_step (MousepadUndo *undo,
-                            gboolean      undo_step)
+mousepad_undo_step_perform (MousepadUndo *undo,
+                            gboolean      redo)
 {
-  MousepadUndoInfo   *info;
+  MousepadUndoStep   *step;
   MousepadUndoAction  action;
+  GList              *li;
   GtkTextIter         start_iter, end_iter;
 
-  _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo));
-
-  /* prevent undo updates */
+  /* lock for updates */
   mousepad_undo_lock (undo);
 
-  /* flush the undo buffer */
-  mousepad_undo_new_step (undo);
-  undo->step_start = undo->step_end = 0;
+  /* flush the cache */
+  mousepad_undo_cache_to_step (undo);
 
-  /* decrease the position counter if we're going to undo */
-  if (undo_step)
-    undo->steps_position--;
+  /* set the previous element for redoing */
+  if (redo)
+    {
+      if (undo->needle)
+        undo->needle = g_list_previous (undo->needle);
+      else
+        undo->needle = g_list_last (undo->steps);
+    }
 
-  /* get the step we're going to undo */
-  info = g_list_nth_data (undo->steps, undo->steps_position);
+  /* freeze buffer notifications */
+  g_object_freeze_notify (G_OBJECT (undo->buffer));
 
-  if (G_LIKELY (info))
+  for (li = undo->needle; li != NULL; li = redo ? li->prev : li->next)
     {
-      /* get the action */
-      action = info->action;
+      /* get the step data */
+      step = li->data;
 
-      /* swap the action if we're going to undo */
-      if (!undo_step)
-        action = (action == INSERT ? DELETE : INSERT);
+      if (G_LIKELY (step))
+        {
+          /* get the action */
+          action = step->action;
 
-      /* get the start iter position */
-      gtk_text_buffer_get_iter_at_offset (undo->buffer, &start_iter, info->start);
+          /* invert the action if we redo  */
+          if (redo)
+            action = (action == INSERT ? DELETE : INSERT);
 
-      switch (action)
-        {
-          case INSERT:
-            /* get the end iter */
-            gtk_text_buffer_get_iter_at_offset (undo->buffer, &end_iter, info->end);
+          /* get the start iter position */
+          gtk_text_buffer_get_iter_at_offset (undo->buffer, &start_iter, step->start);
 
-            /* set the string */
-            if (info->string == NULL)
-              info->string = gtk_text_buffer_get_slice (undo->buffer, &start_iter, &end_iter, TRUE);
+          switch (action)
+            {
+              case INSERT:
+                /* get the end iter */
+                gtk_text_buffer_get_iter_at_offset (undo->buffer, &end_iter, step->end);
 
-            /* delete the inserted text */
-            gtk_text_buffer_delete (undo->buffer, &start_iter, &end_iter);
+                /* set the string we're going to remove for redo */
+                if (step->data == NULL)
+                  step->data = gtk_text_buffer_get_slice (undo->buffer, &start_iter, &end_iter, TRUE);
 
-            break;
+                /* delete the inserted text */
+                gtk_text_buffer_delete (undo->buffer, &start_iter, &end_iter);
+                break;
 
-          case DELETE:
-            /* insert the deleted text */
-            gtk_text_buffer_insert (undo->buffer, &start_iter, info->string, -1);
+              case DELETE:
+                _mousepad_return_if_fail (step->data != NULL);
 
-            /* cleanup the buffer if the user did a redo step (so an inverted insert step) */
-            if (!undo_step)
-              {
-                /* free and reset */
-                g_free (info->string);
-                info->string = NULL;
-              }
+                /* insert the deleted text */
+                gtk_text_buffer_insert (undo->buffer, &start_iter, (gchar *)step->data, -1);
+                break;
 
-            break;
+              default:
+                _mousepad_assert_not_reached ();
+                break;
+            }
 
-          default:
-            _mousepad_assert_not_reached ();
+          /* set the cursor, we scroll to the cursor in mousepad-document */
+          gtk_text_buffer_place_cursor (undo->buffer, &start_iter);
         }
 
-      /* set the cursor, we scroll to the cursor in mousepad-document */
-      gtk_text_buffer_place_cursor (undo->buffer, &start_iter);
+      if (redo)
+        {
+          /* get the previous step to see if it's part of a group */
+          if (g_list_previous (li) != NULL)
+            step = g_list_previous (li)->data;
+          else
+            step = NULL;
+        }
 
-      /* increase the position counter if we did a redo */
-      if (!undo_step)
-        undo->steps_position++;
+      /* as long as the step is part of a group, we continue */
+      if (step == NULL || step->in_group == FALSE)
+        break;
     }
 
-  /* whether we can undo and redo */
-  undo->can_undo = (undo->steps_position > 0);
-  undo->can_redo = (undo->steps_position < g_list_length (undo->steps));
+  /* thawn buffer notifications */
+  g_object_thaw_notify (G_OBJECT (undo->buffer));
+
+  /* set the needle element */
+  if (redo)
+    undo->needle = li;
+  else
+    undo->needle = g_list_next (li);
 
-  /* emit the can-undo and can-redo signals */
-  g_signal_emit (G_OBJECT (undo), undo_signals[CAN_UNDO], 0, undo->can_undo);
-  g_signal_emit (G_OBJECT (undo), undo_signals[CAN_REDO], 0, undo->can_redo);
+  /* emit undo and redo signals */
+  mousepad_undo_emit_signals (undo);
 
-  /* remove our lock */
+  /* release the lock */
   mousepad_undo_unlock (undo);
 }
 
 
 
 static void
-mousepad_undo_new_step (MousepadUndo *undo)
+mousepad_undo_cache_to_step (MousepadUndo *undo)
 {
-  MousepadUndoInfo *info, *existing;
-  gint              i, length;
+  MousepadUndoStep *step;
 
-  /* leave when there is nothing todo */
-  if (undo->step_start == 0 && undo->step_end == 0)
+  /* leave when the cache is empty */
+  if (undo->cache.start == undo->cache.end)
     return;
 
-  length = g_list_length (undo->steps);
-
-  /* when we're not at the end of the list, the user did some redo steps. so we actually
-   * reverted the steps after the current position. if we want to do proper undoing after
-   * appending the current step, we have to append the redo-ed steps in reversed order
-   * with an inverted action.
-   *
-   * there is also a lazy way to avoid weird undo's/redo's: remove all the steps after the
-   * current list position. this was the old behaviour of Mousepad. */
-
-  if (undo->steps_position != length)
-    for (i = length - 1; i >= undo->steps_position; i--)
-      {
-        /* get the existing step */
-        existing = g_list_nth_data (undo->steps, i);
-
-        /* allocate a new slice */
-        info = g_slice_new0 (MousepadUndoInfo);
-
-        /* copy the data from the existing step, ignore delete
-         * action string since they will be inverted to insert
-         * actions*/
-        if (existing->action == INSERT)
-          info->string = g_strdup (existing->string);
-        else
-          info->string = NULL;
-
-        /* set the start and end position */
-        info->start = existing->start;
-        info->end   = existing->end;
-
-        /* set the inverted action */
-        info->action = (existing->action == INSERT ? DELETE : INSERT);
-
-        /* append to the steps list */
-        undo->steps = g_list_append (undo->steps, info);
-      }
-
-  /* allocate the slice */
-  info = g_slice_new0 (MousepadUndoInfo);
-
-  /* set the action, start- and end-position */
-  info->action = undo->step_action;
-  info->start  = undo->step_start;
-  info->end    = undo->step_end;
-
-  /* only set the string if we delete text. if we insert text the
-   * string will be set before we delete it when undoing */
-  if (undo->step_action == INSERT)
-    info->string = NULL;
+  /* allocate slice */
+  step = g_slice_new0 (MousepadUndoStep);
+
+  /* set step data */
+  step->action = undo->cache.action;
+  step->start = undo->cache.start;
+  step->end = undo->cache.end;
+  step->in_group = undo->cache.in_group;
+
+  if (step->action == DELETE)
+    {
+      /* set the step string and allocate a new one */
+      step->data = g_string_free (undo->cache.string, FALSE);
+      undo->cache.string = NULL;
+    }
   else
-    info->string = g_strdup (undo->step_buffer->str);
+    {
+      /* set the data to null on insert actions */
+      step->data = NULL;
+    }
 
-  /* append to the steps list */
-  undo->steps = g_list_append (undo->steps, info);
+  /* prepend the new step */
+  undo->steps = g_list_prepend (undo->steps, step);
 
-  /* erase the buffer */
-  undo->step_buffer = g_string_erase (undo->step_buffer, 0, -1);
-  undo->step_start = undo->step_end = 0;
+  /* reset the needle */
+  undo->needle = undo->steps;
 
-  /* check the list length */
-  for (i = g_list_length (undo->steps); i > MAX_UNDO_STEPS; i--)
-    {
-      /* get the first item in the list */
-      info = g_list_first (undo->steps)->data;
+  /* reset the cache */
+  undo->cache.start = undo->cache.end = -1;
+  undo->cache.is_space = FALSE;
+  undo->cache.in_group = FALSE;
+}
 
-      /* cleanup the data and remove from the list */
-      mousepad_undo_free_step (info, undo);
-    }
 
-  /* set the new list position (end of the list) */
-  undo->steps_position = i;
 
-  /* we're at the end of the list, so we can't redo */
-  undo->can_redo = FALSE;
+static void
+mousepad_undo_cache_reset_needle (MousepadUndo *undo)
+{
+  gint   i;
+
+  /* make sure the needle is the start of the list */
+  if (undo->needle != undo->steps)
+    {
+      /* walk from the start to the needle and remove them */
+      for (i = g_list_position (undo->steps, undo->needle); i > 0; i--)
+        mousepad_undo_step_free (g_list_first (undo->steps)->data, undo);
+
+      /* check needle */
+      _mousepad_return_if_fail (undo->needle == undo->needle);
+    }
 }
 
 
 
 static void
-mousepad_undo_handle_step (const gchar        *text,
-                           gint                start,
-                           gint                end,
-                           MousepadUndoAction  action,
-                           MousepadUndo       *undo)
+mousepad_undo_cache_update (MousepadUndo       *undo,
+                            MousepadUndoAction  action,
+                            gint                start,
+                            gint                end,
+                            const gchar        *text)
 {
-  gint     length;
-  guchar   c;
-  gboolean char_isspace = FALSE;
-  gboolean char_isnewline = FALSE;
-  gboolean create_new_step = FALSE;
-  gboolean clear_step_afterwards = FALSE;
+  gint   length;
+  guchar c;
 
   /* length of the text */
   length = ABS (end - start);
 
-  /* only do this if there is 1 character typed / deleted */
-  if (length == 1)
-    {
-      /* get the character */
-      c = g_utf8_get_char (text);
-
-      /* check if the charater is a space or a new line */
-      char_isspace = (c == ' ' || c == '\t');
-      char_isnewline = (c == '\n');
-    }
+  /* initialize the cache, if not already done */
+  if (undo->cache.string == NULL && action == DELETE)
+    undo->cache.string = g_string_sized_new (DEFAULT_CACHE_SIZE);
 
-  /* create a new step if we jump to a new line or the string contains only spaces, but the
-   * new char is not a space */
-  if (char_isnewline || (undo->step_isspaces && !char_isspace))
+  /* check if we should start a new step before handling this one */
+  if (undo->grouping == 0)
     {
-      create_new_step = clear_step_afterwards = TRUE;
-      goto new_step;
+      if (undo->cache.in_group == TRUE)
+        {
+          /* force a new step if the new step is not part of a group and the
+           * content in the cache is */
+          goto force_new_step;
+        }
+      else if (length == 1)
+        {
+          /* get the character */
+          c = g_utf8_get_char (text);
+
+          /* detect if the character is a word breaking char */
+          if (g_unichar_isspace (c))
+            {
+              /* the char is a word breaker. we don't care if the previous
+               * one was one too, because we merge multiple spaces/tabs/newlines
+               * into one step */
+              undo->cache.is_space = TRUE;
+            }
+          else if (undo->cache.is_space)
+            {
+              /* the new character is not a work breaking char, but the
+               * previous one was, force a new step */
+              goto force_new_step;
+            }
+        }
+      else
+        {
+          /* grouping is not enabled and multiple chars are inseted or
+           * deleted, force a new step */
+          goto force_new_step;
+        }
     }
-  /* if the buffer does not start with a space, but the new char does, we flush the buffer
-   * afterwards */
-  else if (!undo->step_isspaces && char_isspace)
-    clear_step_afterwards = TRUE;
 
-  /* check if we can append (insert action) */
-  if (undo->step_action == action && action == INSERT && undo->step_end == start)
+  /* handle the buffer change and try to append it to the cache */
+  if (undo->cache.action == action
+      && action == INSERT
+      && undo->cache.end == start)
     {
-      /* update the end position. we don't have to prepend the inserted text since
-       * we don't store this when creating a new step */
-      undo->step_end = end;
+      /* we can append with the previous insert change, update end postion */
+      undo->cache.end = end;
     }
-  /* check if we can prepend (delete action) */
-  else if (undo->step_action == action && action == DELETE && undo->step_start == end)
+  else if (undo->cache.action == action
+           && action == DELETE
+           && undo->cache.start == end)
     {
-      /* prepend the deleted text */
-      undo->step_buffer = g_string_prepend_len (undo->step_buffer, text, length);
+      /* we can append with the previous delete change, update */
+      undo->cache.start = start;
 
-      /* update the start position */
-      undo->step_start = start;
+      /* prepend removed characters */
+      undo->cache.string = g_string_prepend_len (undo->cache.string, text, length);
     }
-  /* create a new step */
   else
     {
-      create_new_step = clear_step_afterwards = TRUE;
-    }
+      /* label */
+      force_new_step:
 
-new_step:
-  if (clear_step_afterwards)
-    mousepad_undo_new_step (undo);
+      /* reset the needle */
+      mousepad_undo_cache_reset_needle (undo);
 
-  /* only start a new step when the char was not a space */
-  if (create_new_step)
-    {
-      /* start building a new string if we delete text */
-      if (action == DELETE)
-        undo->step_buffer = g_string_append_len (undo->step_buffer, text, length);
+      /* we cannot cache with the previous change, put the cache into a step */
+      mousepad_undo_cache_to_step (undo);
 
-      /* set the new info */
-      undo->step_action   = action;
-      undo->step_start    = start;
-      undo->step_end      = end;
-      undo->step_isspaces = char_isspace;
+      /* start a new cache */
+      undo->cache.action = action;
+      undo->cache.start = start;
+      undo->cache.end = end;
+      undo->cache.in_group = (undo->grouping > 0);
 
-      /* flush again when we had a new line */
-      if (char_isnewline)
-        mousepad_undo_new_step (undo);
+      if (action == DELETE)
+        {
+          undo->cache.string = g_string_sized_new (DEFAULT_CACHE_SIZE);
+          undo->cache.string = g_string_prepend_len (undo->cache.string, text, length);
+        }
     }
 
-  /* check if we need to send the undo signal */
+  /* increase the grouping counter */
+  undo->grouping++;
+
+  /* stuff has been added to the cache, so we can undo now */
   if (undo->can_undo != TRUE)
     {
       undo->can_undo = TRUE;
@@ -505,55 +560,75 @@ new_step:
 
 
 static void
-mousepad_undo_insert_text (GtkTextBuffer *buffer,
-                           GtkTextIter   *pos,
-                           const gchar   *text,
-                           gint           length,
-                           MousepadUndo  *undo)
+mousepad_undo_buffer_insert (GtkTextBuffer *buffer,
+                             GtkTextIter   *pos,
+                             const gchar   *text,
+                             gint           length,
+                             MousepadUndo  *undo)
 {
   gint start, end;
 
-  /* quit when the undo manager is locked */
-  if (undo->locked)
+  _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+  _mousepad_return_if_fail (buffer == undo->buffer);
+
+  /* leave when locked */
+  if (G_UNLIKELY (undo->locked > 0))
     return;
 
-  /* get the start and end position */
+  /* buffer positions */
   start = gtk_text_iter_get_offset (pos);
   end = start + length;
 
-  /* append the changes */
-  mousepad_undo_handle_step (text, start, end, INSERT, undo);
+  /* update the cache */
+  mousepad_undo_cache_update (undo, INSERT, start, end, text);
 }
 
 
 
 static void
-mousepad_undo_delete_range (GtkTextBuffer *buffer,
-                            GtkTextIter   *start_iter,
-                            GtkTextIter   *end_iter,
-                            MousepadUndo  *undo)
+mousepad_undo_buffer_delete (GtkTextBuffer *buffer,
+                             GtkTextIter   *start_iter,
+                             GtkTextIter   *end_iter,
+                             MousepadUndo  *undo)
 {
-  gint   start, end;
   gchar *text;
+  gint   start, end;
+
+  _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+  _mousepad_return_if_fail (buffer == undo->buffer);
 
-  /* quit when the undo manager is locked */
-  if (undo->locked)
+  /* leave when locked */
+  if (G_UNLIKELY (undo->locked > 0))
     return;
 
-  /* get the start and end position */
+  /* get the removed string */
+  text = gtk_text_buffer_get_slice (buffer, start_iter, end_iter, FALSE);
+
+  /* buffer positions */
   start = gtk_text_iter_get_offset (start_iter);
   end = gtk_text_iter_get_offset (end_iter);
 
-  /* get the deleted string */
-  text = gtk_text_buffer_get_slice (buffer, start_iter, end_iter, FALSE);
-
-  /* append the changes */
-  mousepad_undo_handle_step (text, start, end, DELETE, undo);
+  /* update the cache */
+  mousepad_undo_cache_update (undo, DELETE, start, end, text);
 
   /* cleanup */
   g_free (text);
 }
 
+static void
+mousepad_undo_buffer_begin_user_action (GtkTextBuffer *buffer,
+                                        MousepadUndo  *undo)
+{
+  _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+  _mousepad_return_if_fail (buffer == undo->buffer);
+
+  /* leave when locked */
+  if (G_UNLIKELY (undo->locked > 0))
+    return;
+
+  /* reset the grouping couter */
+  undo->grouping = 0;
+}
 
 
 MousepadUndo *
@@ -566,12 +641,13 @@ mousepad_undo_new (GtkTextBuffer *buffer)
   /* create the undo object */
   undo = g_object_new (MOUSEPAD_TYPE_UNDO, NULL);
 
-  /* set the buffer (we also take a reference) */
+  /* set the buffer with a reference */
   undo->buffer = g_object_ref (G_OBJECT (buffer));
 
   /* connect signals to the buffer so we can monitor it */
-  g_signal_connect (G_OBJECT (buffer), "insert-text", G_CALLBACK (mousepad_undo_insert_text), undo);
-  g_signal_connect (G_OBJECT (buffer), "delete-range", G_CALLBACK (mousepad_undo_delete_range), undo);
+  g_signal_connect (G_OBJECT (buffer), "insert-text", G_CALLBACK (mousepad_undo_buffer_insert), undo);
+  g_signal_connect (G_OBJECT (buffer), "delete-range", G_CALLBACK (mousepad_undo_buffer_delete), undo);
+  g_signal_connect (G_OBJECT (buffer), "begin-user-action", G_CALLBACK (mousepad_undo_buffer_begin_user_action), undo);
 
   return undo;
 }
@@ -579,6 +655,37 @@ mousepad_undo_new (GtkTextBuffer *buffer)
 
 
 void
+mousepad_undo_clear (MousepadUndo *undo)
+{
+  GList *li;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo));
+
+  /* lock to avoid updates */
+  mousepad_undo_lock (undo);
+
+  /* clear cache string */
+  if (G_LIKELY (undo->cache.string))
+    g_string_free (undo->cache.string, TRUE);
+
+  /* cleanup the undo steps */
+  for (li = undo->steps; li != NULL; li = li->next)
+    mousepad_undo_step_free (li->data, undo);
+
+  /* free the list */
+  g_list_free (undo->steps);
+
+  /* null */
+  undo->steps = undo->needle = NULL;
+  undo->cache.string = NULL;
+
+  /* release lock */
+  mousepad_undo_unlock (undo);
+}
+
+
+
+void
 mousepad_undo_lock (MousepadUndo *undo)
 {
   _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo));
@@ -601,40 +708,46 @@ mousepad_undo_unlock (MousepadUndo *undo)
 
 
 
-void
-mousepad_undo_do_undo (MousepadUndo *undo)
+gboolean
+mousepad_undo_can_undo (MousepadUndo *undo)
 {
-  /* run the undo step */
-  mousepad_undo_preform_step (undo, TRUE);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_UNDO (undo), FALSE);
+
+  return undo->can_undo;
 }
 
 
 
-void
-mousepad_undo_do_redo (MousepadUndo *undo)
+gboolean
+mousepad_undo_can_redo (MousepadUndo *undo)
 {
-  /* run the redo step */
-  mousepad_undo_preform_step (undo, FALSE);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_UNDO (undo), FALSE);
+
+  return undo->can_redo;
 }
 
 
 
-gboolean
-mousepad_undo_can_undo (MousepadUndo *undo)
+void
+mousepad_undo_do_undo (MousepadUndo *undo)
 {
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_UNDO (undo), FALSE);
+  _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo));
+  _mousepad_return_if_fail (mousepad_undo_can_undo (undo));
 
-  return undo->can_undo;
+  /* undo the last step */
+  mousepad_undo_step_perform (undo, FALSE);
 }
 
 
 
-gboolean
-mousepad_undo_can_redo (MousepadUndo *undo)
+void
+mousepad_undo_do_redo (MousepadUndo *undo)
 {
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_UNDO (undo), FALSE);
+  _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo));
+  _mousepad_return_if_fail (mousepad_undo_can_redo (undo));
 
-  return undo->can_redo;
+  /* redo the last undo-ed step */
+  mousepad_undo_step_perform (undo, TRUE);
 }
 
 
@@ -648,6 +761,8 @@ mousepad_undo_populate_popup (GtkTextView  *textview,
   gboolean   editable;
 
   _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo));
+  _mousepad_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+  _mousepad_return_if_fail (GTK_IS_MENU (menu));
 
   /* whether the textview is editable */
   editable = gtk_text_view_get_editable (textview);
diff --git a/mousepad/mousepad-undo.h b/mousepad/mousepad-undo.h
index afbb7b3..15cb44e 100644
--- a/mousepad/mousepad-undo.h
+++ b/mousepad/mousepad-undo.h
@@ -34,18 +34,20 @@ GType          mousepad_undo_get_type        (void) G_GNUC_CONST;
 
 MousepadUndo  *mousepad_undo_new             (GtkTextBuffer *buffer);
 
+void           mousepad_undo_clear           (MousepadUndo  *undo);
+
 void           mousepad_undo_lock            (MousepadUndo  *undo);
 
 void           mousepad_undo_unlock          (MousepadUndo  *undo);
 
-void           mousepad_undo_do_undo         (MousepadUndo  *undo);
-
-void           mousepad_undo_do_redo         (MousepadUndo  *undo);
-
 gboolean       mousepad_undo_can_undo        (MousepadUndo  *undo);
 
 gboolean       mousepad_undo_can_redo        (MousepadUndo  *undo);
 
+void           mousepad_undo_do_undo         (MousepadUndo  *undo);
+
+void           mousepad_undo_do_redo         (MousepadUndo  *undo);
+
 void           mousepad_undo_populate_popup  (GtkTextView   *textview,
                                               GtkMenu       *menu,
                                               MousepadUndo  *undo);
diff --git a/mousepad/mousepad-util.c b/mousepad/mousepad-util.c
new file mode 100644
index 0000000..28316c8
--- /dev/null
+++ b/mousepad/mousepad-util.c
@@ -0,0 +1,615 @@
+/* $Id$ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-util.h>
+
+
+
+GtkWidget *
+mousepad_util_image_button (const gchar *stock_id,
+                            const gchar *label)
+{
+  GtkWidget *button, *image;
+
+  image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+  gtk_widget_show (image);
+
+  button = gtk_button_new_with_mnemonic (label);
+  gtk_button_set_image (GTK_BUTTON (button), image);
+  gtk_widget_show (button);
+
+  return button;
+}
+
+
+
+void
+mousepad_util_entry_error (GtkWidget *widget,
+                           gboolean   error)
+{
+  const GdkColor red   = {0, 0xffff, 0x6666, 0x6666};
+  const GdkColor white = {0, 0xffff, 0xffff, 0xffff};
+  gpointer       pointer;
+
+  _mousepad_return_if_fail (GTK_IS_WIDGET (widget));
+
+  /* get the current error state */
+  pointer = g_object_get_data (G_OBJECT (widget), I_("error-state"));
+
+  /* only change the state when really needed to avoid multiple widget calls */
+  if (GPOINTER_TO_INT (pointer) != error)
+    {
+      /* set the widget style */
+      gtk_widget_modify_base (widget, GTK_STATE_NORMAL, error ? &red : NULL);
+      gtk_widget_modify_text (widget, GTK_STATE_NORMAL, error ? &white : NULL);
+
+      /* set the new state */
+      g_object_set_data (G_OBJECT (widget), I_("error-state"), GINT_TO_POINTER (error));
+    }
+}
+
+
+
+void
+mousepad_util_dialog_header (GtkDialog   *dialog,
+                             const gchar *title,
+                             const gchar *subtitle,
+                             const gchar *iconname)
+{
+  gchar     *full_title;
+  GtkWidget *vbox, *ebox, *hbox;
+  GtkWidget *icon, *label, *line;
+
+  /* remove the main vbox */
+  g_object_ref (G_OBJECT (dialog->vbox));
+  gtk_container_remove (GTK_CONTAINER (dialog), dialog->vbox);
+
+  /* add a new vbox to the main window */
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (dialog), vbox);
+  gtk_widget_show (vbox);
+
+  /* event box for the background color */
+  ebox = gtk_event_box_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), ebox, FALSE, FALSE, 0);
+  gtk_widget_modify_bg (ebox, GTK_STATE_NORMAL, &ebox->style->base[GTK_STATE_NORMAL]);
+  gtk_widget_show (ebox);
+
+  /* create a hbox */
+  hbox = gtk_hbox_new (FALSE, 12);
+  gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+  gtk_container_add (GTK_CONTAINER (ebox), hbox);
+  gtk_widget_show (hbox);
+
+  /* title icon */
+  icon = gtk_image_new_from_icon_name (iconname, GTK_ICON_SIZE_DIALOG);
+  gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
+  gtk_widget_show (icon);
+
+  /* create the title */
+  full_title = g_strdup_printf ("<b><big>%s</big></b>\n%s", title, subtitle);
+
+  /* title label */
+  label = gtk_label_new (full_title);
+  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+  gtk_widget_show (label);
+
+  /* cleanup */
+  g_free (full_title);
+
+  /* add the separator between header and content */
+  line = gtk_hseparator_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), line, FALSE, FALSE, 0);
+  gtk_widget_show (line);
+
+  /* add the main dialog box to the new vbox */
+  gtk_box_pack_start (GTK_BOX (vbox), GTK_DIALOG (dialog)->vbox, TRUE, TRUE, 0);
+  g_object_unref (G_OBJECT (GTK_DIALOG (dialog)->vbox));
+}
+
+
+
+void
+mousepad_util_set_tooltip (GtkWidget   *widget,
+                           const gchar *string)
+{
+  static GtkTooltips *tooltips = NULL;
+
+  _mousepad_return_if_fail (GTK_IS_WIDGET (widget));
+  _mousepad_return_if_fail (string ? g_utf8_validate (string, -1, NULL) : TRUE);
+
+  /* allocate the shared tooltips on-demand */
+  if (G_UNLIKELY (tooltips == NULL))
+    tooltips = gtk_tooltips_new ();
+
+  /* setup the tooltip for the widget */
+  gtk_tooltips_set_tip (tooltips, widget, string, NULL);
+}
+
+
+
+gint
+mousepad_util_get_real_line_offset (const GtkTextIter *iter,
+                                    gint               tab_width)
+{
+  gint        offset = 0;
+  GtkTextIter needle = *iter;
+
+  gtk_text_iter_set_line_offset (&needle, 0);
+
+  while (!gtk_text_iter_equal (&needle, iter))
+    {
+      if (gtk_text_iter_get_char (&needle) == '\t')
+        offset += (tab_width - (offset % tab_width));
+      else
+        offset++;
+
+      gtk_text_iter_forward_char (&needle);
+    }
+
+  return offset;
+}
+
+
+
+GType
+mousepad_util_search_flags_get_type (void)
+{
+	static GType type = G_TYPE_NONE;
+
+	if (G_UNLIKELY (type == G_TYPE_NONE))
+	  {
+	    /* use empty values table */
+	    static const GFlagsValue values[] =
+	    {
+          { 0, NULL, NULL }
+	    };
+
+	    /* register the type */
+	    type = g_flags_register_static (I_("MousepadSearchFlags"), values);
+      }
+
+  return type;
+}
+
+
+
+static gboolean
+mousepad_util_search_iter (const GtkTextIter   *start,
+                           const gchar         *string,
+                           MousepadSearchFlags  flags,
+                           GtkTextIter         *match_start,
+                           GtkTextIter         *match_end,
+                           const GtkTextIter   *limit)
+{
+  GtkTextIter  iter, begin;
+  gunichar     iter_char, str_char;
+  gboolean     succeed = FALSE;
+  const gchar *needle = string;
+  gboolean     match_case, search_backwards, whole_word;
+  guint        needle_offset = 0;
+
+  _mousepad_return_val_if_fail (start != NULL, FALSE);
+  _mousepad_return_val_if_fail (string != NULL, FALSE);
+  _mousepad_return_val_if_fail (limit != NULL, FALSE);
+
+  /* search properties */
+  match_case       = (flags & MOUSEPAD_SEARCH_FLAGS_MATCH_CASE) != 0;
+  search_backwards = (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD) != 0;
+  whole_word       = (flags & MOUSEPAD_SEARCH_FLAGS_WHOLE_WORD) != 0;
+
+  /* set the start iter */
+  iter = *start;
+
+  /* walk from the start to the end iter */
+  do
+    {
+      /* break when we reach the limit iter */
+      if (G_UNLIKELY (gtk_text_iter_equal (&iter, limit)))
+        break;
+
+      /* get the unichar characters */
+      iter_char = gtk_text_iter_get_char (&iter);
+      str_char  = g_utf8_get_char (needle);
+
+      /* skip unknown characters */
+      if (G_UNLIKELY (iter_char == 0xFFFC))
+        goto continue_searching;
+
+      /* lower case searching */
+      if (!match_case)
+        {
+          /* convert the characters to lower case */
+          iter_char = g_unichar_tolower (iter_char);
+          str_char  = g_unichar_tolower (str_char);
+        }
+
+      /* compare the two characters */
+      if (iter_char == str_char)
+        {
+          /* first character matched, set the begin iter */
+          if (needle_offset == 0)
+            begin = iter;
+
+          /* get the next character and increase the offset counter */
+          needle = g_utf8_next_char (needle);
+          needle_offset++;
+
+          /* we hit the end of the search string, so we had a full match */
+          if (G_UNLIKELY (*needle == '\0'))
+            {
+              if (G_LIKELY (!search_backwards))
+                {
+                  /* set the end iter after the character (for selection) */
+                  gtk_text_iter_forward_char (&iter);
+
+                  /* check if we match a whole word */
+                  if (whole_word && !(gtk_text_iter_starts_word (&begin) && gtk_text_iter_ends_word (&iter)))
+                    goto reset_and_continue_searching;
+                }
+              else
+                {
+                  /* set the start iter after the character (for selection) */
+                  gtk_text_iter_forward_char (&begin);
+
+                  /* check if we match a whole word */
+                  if (whole_word && !(gtk_text_iter_starts_word (&iter) && gtk_text_iter_ends_word (&begin)))
+                    goto reset_and_continue_searching;
+                }
+
+              /* set the start and end iters */
+              *match_start = begin;
+              *match_end   = iter;
+
+              /* return true and stop searching */
+              succeed = TRUE;
+
+              break;
+            }
+        }
+      else if (needle_offset > 0)
+        {
+          /* label */
+          reset_and_continue_searching:
+
+          /* reset the needle */
+          needle = string;
+          needle_offset = 0;
+
+          /* reset the iter */
+          iter = begin;
+        }
+
+      /* label */
+      continue_searching:
+
+      /* jump to next iter in the buffer */
+      if ((!search_backwards && !gtk_text_iter_forward_char (&iter))
+          || (search_backwards && !gtk_text_iter_backward_char (&iter)))
+        break;
+    }
+  while (TRUE);
+
+  return succeed;
+}
+
+
+
+static void
+mousepad_util_search_get_iters (GtkTextBuffer       *buffer,
+                                MousepadSearchFlags  flags,
+                                GtkTextIter         *start,
+                                GtkTextIter         *end,
+                                GtkTextIter         *iter)
+{
+  GtkTextIter sel_start, sel_end, tmp;
+
+  /* get selection bounds */
+  gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end);
+
+  if (flags & MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT)
+    {
+      /* get document bounds */
+      gtk_text_buffer_get_bounds (buffer, start, end);
+
+      /* set the start iter */
+      if (flags & MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START)
+        *iter = *start;
+      else if (flags & MOUSEPAD_SEARCH_FLAGS_ITER_AREA_END)
+        *iter = *end;
+      else
+        goto set_selection_iter;
+    }
+  else if (flags & MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION)
+    {
+      /* set area iters */
+      *start = sel_start;
+      *end = sel_end;
+
+      set_selection_iter:
+
+      /* set the start iter */
+      if (flags & (MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START | MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START))
+        *iter = sel_start;
+      else if (flags & (MOUSEPAD_SEARCH_FLAGS_ITER_AREA_END | MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END))
+        *iter = sel_end;
+      else
+        _mousepad_assert_not_reached ();
+    }
+  else
+    {
+      /* this should never happen */
+      _mousepad_assert_not_reached ();
+    }
+
+  /* invert the start and end iter on backwards searching */
+  if (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD)
+    {
+      tmp = *start;
+      *start = *end;
+      *end = tmp;
+    }
+}
+
+
+
+gint
+mousepad_util_highlight (GtkTextBuffer       *buffer,
+                         GtkTextTag          *tag,
+                         const gchar         *string,
+                         MousepadSearchFlags  flags)
+{
+  GtkTextIter start, iter, end;
+  GtkTextIter match_start, match_end;
+  GtkTextIter cache_start, cache_end;
+  gboolean    found, cached = FALSE;
+  gint        counter = 0;
+
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), -1);
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_TAG (tag), -1);
+  _mousepad_return_val_if_fail (string == NULL || g_utf8_validate (string, -1, NULL), -1);
+  _mousepad_return_val_if_fail ((flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD) == 0, -1);
+
+  /* get the buffer bounds */
+  gtk_text_buffer_get_bounds (buffer, &start, &end);
+
+  /* remove all the highlight tags */
+  gtk_text_buffer_remove_tag (buffer, tag, &start, &end);
+
+  /* quit if there is nothing to highlight */
+  if (string == NULL || *string == '\0' || flags & MOUSEPAD_SEARCH_FLAGS_ACTION_CLEANUP)
+    return 0;
+
+  /* get the search iters */
+  mousepad_util_search_get_iters (buffer, flags, &start, &end, &iter);
+
+  /* initialize cache iters */
+  cache_start = cache_end = iter;
+
+  /* highlight all the occurences of the strings */
+  do
+    {
+      /* search for the next occurence of the string */
+      found = mousepad_util_search_iter (&iter, string, flags, &match_start, &match_end, &end);
+
+      if (G_LIKELY (found))
+        {
+          /* try to extend the cache */
+          if (gtk_text_iter_equal (&cache_end, &match_start))
+            {
+              /* enable caching */
+              cached = TRUE;
+            }
+          else
+            {
+              if (cached)
+                {
+                  /* highlight the cached occurences */
+                  gtk_text_buffer_apply_tag (buffer, tag, &cache_start, &cache_end);
+
+                  /* cache is flushed */
+                  cached = FALSE;
+                }
+
+              /* highlight the matched occurence */
+              gtk_text_buffer_apply_tag (buffer, tag, &match_start, &match_end);
+
+              /* set the new cache start iter */
+              cache_start = match_start;
+            }
+
+          /* set the end iters */
+          iter = cache_end = match_end;
+
+          /* increase the counter */
+          counter++;
+        }
+      else if (G_UNLIKELY (cached))
+        {
+          /* flush the cached iters */
+          gtk_text_buffer_apply_tag (buffer, tag, &cache_start, &cache_end);
+        }
+    }
+  while (G_LIKELY (found));
+
+  return counter;
+}
+
+
+
+gint
+mousepad_util_search (GtkTextBuffer       *buffer,
+                      const gchar         *string,
+                      const gchar         *replace,
+                      MousepadSearchFlags  flags)
+{
+  gchar       *reversed = NULL;
+  gint         counter = 0;
+  gboolean     found, search_again = FALSE;
+  gboolean     search_backwards, wrap_around;
+  GtkTextIter  start, end, iter;
+  GtkTextIter  match_start, match_end;
+  GtkTextMark *mark_start, *mark_iter, *mark_end, *mark_replace;
+
+  _mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), -1);
+  _mousepad_return_val_if_fail (string && g_utf8_validate (string, -1, NULL), -1);
+  _mousepad_return_val_if_fail (replace == NULL || g_utf8_validate (replace, -1, NULL), -1);
+  _mousepad_return_val_if_fail ((flags & MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT) == 0, -1);
+
+  /* freeze buffer notifications */
+  g_object_freeze_notify (G_OBJECT (buffer));
+
+  /* get the search iters */
+  mousepad_util_search_get_iters (buffer, flags, &start, &end, &iter);
+
+  /* store the initial iters in marks */
+  mark_start = gtk_text_buffer_create_mark (buffer, NULL, &start, TRUE);
+  mark_iter  = gtk_text_buffer_create_mark (buffer, NULL, &iter, TRUE);
+  mark_end   = gtk_text_buffer_create_mark (buffer, NULL, &end, TRUE);
+
+  /* if we're not really searching anything, reset the cursor */
+  if (string == NULL || *string == '\0')
+    goto reset_cursor;
+
+  /* some to make the code easier to read */
+  search_backwards = (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD) != 0;
+  wrap_around = (flags & MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND) != 0 && !gtk_text_iter_equal (&start, &iter);
+
+  if (search_backwards)
+    {
+      /* reverse the search string */
+      reversed = g_utf8_strreverse (string, -1);
+
+      /* set the new search string */
+      string = reversed;
+    }
+
+  do
+    {
+      /* search the string */
+      found = mousepad_util_search_iter (&iter, string, flags, &match_start, &match_end, &end);
+
+      /* don't search again unless changed below */
+      search_again = FALSE;
+
+      if (found)
+        {
+          /* handle the action */
+          if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT)
+            {
+              /* select the match */
+              gtk_text_buffer_select_range (buffer, &match_start, &match_end);
+            }
+          else if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE)
+	        {
+              /* create text mark */
+              mark_replace = gtk_text_buffer_create_mark (buffer, NULL, &match_start, search_backwards);
+
+              /* delete the match */
+              gtk_text_buffer_delete (buffer, &match_start, &match_end);
+
+              /* restore the iter */
+              gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark_replace);
+
+              /* insert the replacement */
+              if (G_LIKELY (replace))
+                gtk_text_buffer_insert (buffer, &iter, replace, -1);
+
+              /* remove the mark */
+              gtk_text_buffer_delete_mark (buffer, mark_replace);
+
+              /* restore the begin and end iters */
+              gtk_text_buffer_get_iter_at_mark (buffer, &start, mark_start);
+              gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_end);
+
+              /* when we don't replace all matches, select the next one */
+              if ((flags & MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA) == 0)
+                flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT;
+
+              /* search again */
+              search_again = TRUE;
+	        }
+	      else if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_NONE)
+            {
+              /* keep searching when requested */
+              if (flags & MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA)
+                search_again = TRUE;
+
+              /* move iter */
+              iter = match_end;
+            }
+	      else
+	        {
+              /* no valid action was defined */
+              _mousepad_assert_not_reached ();
+            }
+
+          /* increase the counter */
+          counter++;
+        }
+      else if (wrap_around)
+        {
+          /* get the start iter (note that the start and iter were already
+           * reversed for backwards searching) */
+          gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark_start);
+
+          /* get end iter */
+          gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_iter);
+
+          /* we wrapped, don't try again */
+          wrap_around = FALSE;
+
+          /* search again */
+          search_again = TRUE;
+        }
+      else
+        {
+          reset_cursor:
+
+          /* get the initial start mark */
+          gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark_iter);
+
+          /* reset the cursor */
+          gtk_text_buffer_place_cursor (buffer, &iter);
+        }
+    }
+  while (search_again);
+
+  /* make sure the selection is restored */
+  if (flags & MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION)
+    gtk_text_buffer_select_range (buffer, &start, &end);
+
+  /* cleanup */
+  g_free (reversed);
+
+  /* cleanup marks */
+  gtk_text_buffer_delete_mark (buffer, mark_start);
+  gtk_text_buffer_delete_mark (buffer, mark_iter);
+  gtk_text_buffer_delete_mark (buffer, mark_end);
+
+  /* thawn buffer notifications */
+  g_object_thaw_notify (G_OBJECT (buffer));
+
+  return counter;
+}
diff --git a/mousepad/mousepad-util.h b/mousepad/mousepad-util.h
new file mode 100644
index 0000000..4f3f575
--- /dev/null
+++ b/mousepad/mousepad-util.h
@@ -0,0 +1,92 @@
+/* $Id$ */
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __MOUSEPAD_UTIL_H__
+#define __MOUSEPAD_UTIL_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_SEARCH_FLAGS (mousepad_util_search_flags_get_type ())
+
+typedef enum _MousepadSearchFlags MousepadSearchFlags;
+
+enum _MousepadSearchFlags
+{
+  /* search area */
+  MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT     = 1 << 0,  /* search the entire document */
+  MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION    = 1 << 1,  /* search inside selection */
+
+  /* iter start point */
+  MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START   = 1 << 2,  /* search from the beginning of the area */
+  MOUSEPAD_SEARCH_FLAGS_ITER_AREA_END     = 1 << 3,  /* search from the end of the area */
+  MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START    = 1 << 4,  /* start at from the beginning of the selection */
+  MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END      = 1 << 5,  /* start at from the end of the selection */
+
+  /* direction */
+  MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD       = 1 << 6,  /* search forward to end of area */
+  MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD      = 1 << 7,  /* search backwards to start of area */
+
+  /* search settings */
+  MOUSEPAD_SEARCH_FLAGS_MATCH_CASE        = 1 << 8,  /* match case */
+  MOUSEPAD_SEARCH_FLAGS_WHOLE_WORD        = 1 << 9,  /* only match whole words */
+  MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND       = 1 << 10, /* wrap around */
+  MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA       = 1 << 11, /* keep searching until the end of the area is reached */
+  MOUSEPAD_SEARCH_FLAGS_ALL_DOCUMENTS     = 1 << 12, /* search all documents */
+
+
+  /* actions */
+  MOUSEPAD_SEARCH_FLAGS_ACTION_NONE       = 1 << 13, /* no visible actions */
+  MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT = 1 << 14, /* highlight all the occurences */
+  MOUSEPAD_SEARCH_FLAGS_ACTION_CLEANUP    = 1 << 15, /* cleanup the highlighted occurences */
+  MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT     = 1 << 16, /* select the match */
+  MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE    = 1 << 17, /* replace the match */
+};
+
+
+
+GtkWidget *mousepad_util_image_button           (const gchar         *stock_id,
+                                                 const gchar         *label);
+
+void       mousepad_util_entry_error            (GtkWidget           *widget,
+                                                 gboolean             error);
+
+void       mousepad_util_dialog_header          (GtkDialog           *dialog,
+                                                 const gchar         *title,
+                                                 const gchar         *subtitle,
+                                                 const gchar         *icon);
+
+void       mousepad_util_set_tooltip            (GtkWidget           *widget,
+                                                 const gchar         *string);
+
+gint       mousepad_util_get_real_line_offset   (const GtkTextIter   *iter,
+                                                 gint                 tab_width);
+
+GType      mousepad_util_search_flags_get_type  (void) G_GNUC_CONST;
+
+gint       mousepad_util_highlight              (GtkTextBuffer       *buffer,
+                                                 GtkTextTag          *tag,
+                                                 const gchar         *string,
+                                                 MousepadSearchFlags  flags);
+
+gint       mousepad_util_search                 (GtkTextBuffer       *buffer,
+                                                 const gchar         *string,
+                                                 const gchar         *replace,
+                                                 MousepadSearchFlags  flags);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_UTIL_H__ */
diff --git a/mousepad/mousepad-view.c b/mousepad/mousepad-view.c
index 1e90d4b..2c0a7fe 100644
--- a/mousepad/mousepad-view.c
+++ b/mousepad/mousepad-view.c
@@ -26,51 +26,54 @@
 #include <gdk/gdkkeysyms.h>
 
 #include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-util.h>
 #include <mousepad/mousepad-view.h>
 
 
 
 /* we can assume there is a buffer for this view */
-#define mousepad_view_get_buffer(view) (GTK_TEXT_VIEW(view)->buffer)
-
-
-
-/* class functions */
-static void      mousepad_view_class_init                    (MousepadViewClass  *klass);
-static void      mousepad_view_init                          (MousepadView       *view);
-static void      mousepad_view_finalize                      (GObject            *object);
-static void      mousepad_view_style_set                     (GtkWidget          *widget,
-                                                              GtkStyle           *previous_style);
-static gint      mousepad_view_expose                        (GtkWidget          *widget,
-                                                              GdkEventExpose     *event);
-static gboolean  mousepad_view_key_press_event               (GtkWidget          *widget,
-                                                              GdkEventKey        *event);
-static gboolean  mousepad_view_button_press_event            (GtkWidget          *widget,
-                                                              GdkEventButton     *event);
-static gboolean  mousepad_view_button_release_event          (GtkWidget          *widget,
-                                                              GdkEventButton     *event);
-
-/* vertical selection functions */
-static GArray   *mousepad_view_vertical_selection_array      (MousepadView       *view,
-                                                              gboolean            forced);
-static gboolean  mousepad_view_vertical_selection_timeout    (gpointer            user_data);
-static void      mousepad_view_vertical_selection_reset      (MousepadView       *view);
-
-/* indentation functions */
-static void      mousepad_view_indent_line                   (GtkTextBuffer      *buffer,
-                                                              GtkTextIter        *iter,
-                                                              gboolean            increase,
-                                                              gboolean            tab);
-static gboolean  mousepad_view_indent_lines                  (MousepadView       *view,
-                                                              gboolean            indent,
-                                                              gboolean            tab);
-static gchar    *mousepad_view_compute_indentation           (GtkTextBuffer      *buffer,
-                                                              const GtkTextIter  *iter);
-
-/* vertical selection edit handler */
-static void      mousepad_view_handle_clipboard                 (MousepadView       *view,
-                                                              GtkClipboard       *clipboard,
-                                                              gboolean            remove);
+#define mousepad_view_get_buffer(view) (GTK_TEXT_VIEW (view)->buffer)
+
+
+
+static void      mousepad_view_class_init                         (MousepadViewClass  *klass);
+static void      mousepad_view_init                               (MousepadView       *view);
+static void      mousepad_view_finalize                           (GObject            *object);
+static void      mousepad_view_style_set                          (GtkWidget          *widget,
+                                                                   GtkStyle           *previous_style);
+static gint      mousepad_view_expose                             (GtkWidget          *widget,
+                                                                   GdkEventExpose     *event);
+static gboolean  mousepad_view_key_press_event                    (GtkWidget          *widget,
+                                                                   GdkEventKey        *event);
+static gboolean  mousepad_view_button_press_event                 (GtkWidget          *widget,
+                                                                   GdkEventButton     *event);
+static gboolean  mousepad_view_button_release_event               (GtkWidget          *widget,
+                                                                   GdkEventButton     *event);
+static void      mousepad_view_vertical_selection_handle          (MousepadView       *view,
+                                                                   gboolean            prepend_marks);
+static gboolean  mousepad_view_vertical_selection_finish          (MousepadView       *view);
+static void      mousepad_view_vertical_selection_free_marks      (MousepadView       *view);
+static void      mousepad_view_vertical_selection_clear           (MousepadView       *view);
+static gboolean  mousepad_view_vertical_selection_timeout         (gpointer            user_data);
+static void      mousepad_view_vertical_selection_timeout_destroy (gpointer            user_data);
+static void      mousepad_view_increase_indent_iter               (MousepadView       *view,
+                                                                   GtkTextIter        *iter,
+                                                                   gboolean            tab);
+static void      mousepad_view_decrease_indent_iter               (MousepadView       *view,
+                                                                   GtkTextIter        *iter,
+                                                                   gboolean            tab);
+static void      mousepad_view_indent_selection                   (MousepadView       *view,
+                                                                   gboolean            increase,
+                                                                   gboolean            tab);
+
+static gchar    *mousepad_view_indentation_string                 (GtkTextBuffer      *buffer,
+                                                                   const GtkTextIter  *iter);
+static gint      mousepad_view_calculate_layout_width             (GtkWidget          *widget,
+                                                                   gsize               length,
+                                                                   gchar               fill_char);
+static void      mousepad_view_clipboard                          (MousepadView       *view,
+                                                                   GtkClipboard       *clipboard,
+                                                                   gboolean            remove);
 
 
 
@@ -88,10 +91,10 @@ struct _MousepadView
   gint         vertical_end_x, vertical_end_y;
 
   /* pointer array with all the vertical selection marks */
-  GPtrArray   *marks;
+  GSList      *marks;
 
   /* vertical selection timeout id */
-  guint        vertical_selection_id;
+  guint        vertical_timeout_id;
 
   /* the selection tag */
   GtkTextTag  *tag;
@@ -99,6 +102,8 @@ struct _MousepadView
   /* settings */
   guint        auto_indent : 1;
   guint        line_numbers : 1;
+  guint        insert_spaces : 1;
+  guint        tab_width;
 };
 
 
@@ -152,17 +157,20 @@ mousepad_view_class_init (MousepadViewClass *klass)
 static void
 mousepad_view_init (MousepadView *view)
 {
-  /* initialize */
-  view->auto_indent = view->line_numbers = FALSE;
+  /* initialize settings */
+  view->auto_indent = FALSE;
+  view->line_numbers = FALSE;
+  view->insert_spaces = FALSE;
+  view->tab_width = 8;
 
-  /* initialize */
+  /* initialize vertical selection */
   view->marks = NULL;
   view->tag = NULL;
-
-  /* initialize */
-  view->vertical_start_x = view->vertical_start_y =
-  view->vertical_end_x = view->vertical_end_y = -1;
-  view->vertical_selection_id = 0;
+  view->vertical_start_x = -1;
+  view->vertical_start_y = -1;
+  view->vertical_end_x = -1;
+  view->vertical_end_y = -1;
+  view->vertical_timeout_id = 0;
 }
 
 
@@ -173,12 +181,12 @@ mousepad_view_finalize (GObject *object)
   MousepadView *view = MOUSEPAD_VIEW (object);
 
   /* stop a pending vertical selection timeout */
-  if (G_UNLIKELY (view->vertical_selection_id != 0))
-    g_source_remove (view->vertical_selection_id);
+  if (G_UNLIKELY (view->vertical_timeout_id != 0))
+    g_source_remove (view->vertical_timeout_id);
 
-  /* free the marks array (we're not owning a ref on the marks) */
+  /* free the marks list (we're not owning a ref on the marks) */
   if (G_UNLIKELY (view->marks != NULL))
-    g_ptr_array_free (view->marks, TRUE);
+    g_slist_free (view->marks);
 
   (*G_OBJECT_CLASS (mousepad_view_parent_class)->finalize) (object);
 }
@@ -287,24 +295,39 @@ static void
 mousepad_view_style_set (GtkWidget *widget,
                          GtkStyle  *previous_style)
 {
-  GtkStyle     *style;
-  MousepadView *view = MOUSEPAD_VIEW (widget);
+  GtkStyle      *style;
+  GtkTextIter    start, end;
+  MousepadView  *view = MOUSEPAD_VIEW (widget);
+  GtkTextBuffer *buffer;
+
+  /* run widget handler */
+  (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->style_set) (widget, previous_style);
 
-  /* only set the style once, because we don't care to restart
-   * after a theme change */
-  if (view->tag == NULL && GTK_WIDGET_VISIBLE (widget))
+  /* update the style after a real style change */
+  if (previous_style)
     {
       /* get the textview style */
       style = gtk_widget_get_style (widget);
 
-      /* create the selection tag */
-      view->tag = gtk_text_buffer_create_tag (mousepad_view_get_buffer (view), NULL,
+      /* get the text buffer */
+      buffer = mousepad_view_get_buffer (view);
+
+      /* remove previous selection tag */
+      if (G_UNLIKELY (view->tag))
+        {
+          gtk_text_buffer_get_bounds (buffer, &start, &end);
+          gtk_text_buffer_remove_tag (buffer, view->tag, &start, &end);
+        }
+
+      /* create the new selection tag */
+      view->tag = gtk_text_buffer_create_tag (buffer, NULL,
                                               "background-gdk", &style->base[GTK_STATE_SELECTED],
                                               "foreground-gdk", &style->text[GTK_STATE_SELECTED],
                                               NULL);
-    }
 
-  (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->style_set) (widget, previous_style);
+      /* update the tab stops */
+      mousepad_view_set_tab_width (view, view->tab_width);
+    }
 }
 
 
@@ -315,15 +338,17 @@ mousepad_view_key_press_event (GtkWidget   *widget,
 {
   MousepadView  *view = MOUSEPAD_VIEW (widget);
   GtkTextBuffer *buffer;
-  GtkTextIter    start;
+  GtkTextIter    iter;
   GtkTextMark   *mark;
   guint          modifiers;
   gchar         *string;
-  gboolean       key_is_tab = FALSE;
 
   /* get the modifiers state */
   modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
 
+  /* get the textview buffer */
+  buffer = mousepad_view_get_buffer (view);
+
   /* handle the key event */
   switch (event->keyval)
     {
@@ -331,29 +356,32 @@ mousepad_view_key_press_event (GtkWidget   *widget,
       case GDK_KP_Enter:
         if (!(event->state & GDK_SHIFT_MASK) && view->auto_indent)
           {
-            /* get the textview buffer */
-            buffer = mousepad_view_get_buffer (view);
-
             /* get the iter position of the cursor */
             mark = gtk_text_buffer_get_insert (buffer);
-            gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
+            gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
 
             /* get the string of tabs and spaces we're going to indent */
-            string = mousepad_view_compute_indentation (buffer, &start);
+            string = mousepad_view_indentation_string (buffer, &iter);
 
             if (string != NULL)
               {
+                /* begin a user action */
+                gtk_text_buffer_begin_user_action (buffer);
+
                 /* insert the indent characters */
-                gtk_text_buffer_insert (buffer, &start, "\n", 1);
-                gtk_text_buffer_insert (buffer, &start, string, strlen (string));
+                gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+                gtk_text_buffer_insert (buffer, &iter, string, -1);
+
+                /* end user action */
+                gtk_text_buffer_end_user_action (buffer);
 
                 /* cleanup */
                 g_free (string);
 
                 /* make sure the new string is visible for the user */
-                gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (widget), mark);
+                mousepad_view_put_cursor_on_screen (view);
 
-                /* we inserted the new line, nothing to do for gtk */
+                /* we've inserted the new line, nothing to do for gtk */
                 return TRUE;
               }
           }
@@ -362,26 +390,43 @@ mousepad_view_key_press_event (GtkWidget   *widget,
       case GDK_Tab:
       case GDK_KP_Tab:
       case GDK_ISO_Left_Tab:
-        /* indent a tab when no modifiers are pressed, else fall-through */
-        if (modifiers == 0 &&
-            mousepad_view_indent_lines (view, TRUE, TRUE))
-              return TRUE;
+        if (mousepad_view_get_has_selection (view))
+          {
+            /* indent the selection */
+            mousepad_view_indent_selection (view, (modifiers != GDK_SHIFT_MASK), TRUE);
 
-        /* a tab was pressed */
-        key_is_tab = TRUE;
+            return TRUE;
+          }
+        else if (view->insert_spaces)
+          {
+            /* get the iter position of the cursor */
+            mark = gtk_text_buffer_get_insert (buffer);
+            gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+
+            /* insert spaces */
+            mousepad_view_increase_indent_iter (view, &iter, TRUE);
+
+            /* TODO cursor position? */
+
+            return TRUE;
+          }
 
       case GDK_BackSpace:
       case GDK_space:
         /* indent on Space unindent on Backspace or Tab */
-        if (modifiers == GDK_SHIFT_MASK &&
-            mousepad_view_indent_lines (view, (event->keyval == GDK_space), key_is_tab))
-              return TRUE;
+        if (modifiers == GDK_SHIFT_MASK && mousepad_view_get_has_selection (view))
+          {
+            /* indent the selection */
+            mousepad_view_indent_selection (view, (event->keyval == GDK_space), FALSE);
+
+            return TRUE;
+          }
         break;
     }
 
   /* reset the vertical selection */
   if (G_UNLIKELY (view->marks != NULL && !event->is_modifier))
-    mousepad_view_vertical_selection_reset (view);
+    mousepad_view_vertical_selection_clear (view);
 
   return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->key_press_event) (widget, event);
 }
@@ -393,14 +438,11 @@ mousepad_view_button_press_event (GtkWidget      *widget,
                                   GdkEventButton *event)
 {
   MousepadView *view = MOUSEPAD_VIEW (widget);
-  GtkTextView  *text_view;
+  GtkTextView  *textview;
 
-  /* cleanup the marks if needed */
+  /* cleanup vertical selection */
   if (G_UNLIKELY (view->marks != NULL))
-    {
-      /* reset the vertical selection */
-      mousepad_view_vertical_selection_reset (view);
-    }
+    mousepad_view_vertical_selection_clear (view);
 
   /* Shift + Ctrl + Button 1 are pressed */
   if (G_UNLIKELY (event->type == GDK_BUTTON_PRESS
@@ -411,14 +453,15 @@ mousepad_view_button_press_event (GtkWidget      *widget,
                   && event->y >= 0))
     {
       /* get the textview */
-      text_view = GTK_TEXT_VIEW (widget);
+      textview = GTK_TEXT_VIEW (widget);
 
       /* set the vertical selection start position, inclusing textview offset */
-      view->vertical_start_x = event->x + text_view->xoffset;
-      view->vertical_start_y = event->y + text_view->yoffset;
+      view->vertical_start_x = event->x + textview->xoffset;
+      view->vertical_start_y = event->y + textview->yoffset;
 
       /* start a vertical selection timeout */
-      view->vertical_selection_id = g_timeout_add (75, mousepad_view_vertical_selection_timeout, view);
+      view->vertical_timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 50, mousepad_view_vertical_selection_timeout,
+                                                      view, mousepad_view_vertical_selection_timeout_destroy);
 
       return TRUE;
     }
@@ -433,55 +476,18 @@ mousepad_view_button_release_event (GtkWidget      *widget,
                                     GdkEventButton *event)
 {
   MousepadView  *view = MOUSEPAD_VIEW (widget);
-  GArray        *array;
-  gint           i;
-  GtkTextBuffer *buffer;
-  GtkTextIter    iter;
-  GtkTextMark   *mark;
 
   _mousepad_return_val_if_fail (view->marks == NULL, TRUE);
 
   /* end of a vertical selection */
-  if (G_UNLIKELY (view->vertical_start_x != -1
-                  && event->button == 1))
+  if (G_UNLIKELY (view->vertical_start_x != -1 && event->button == 1))
     {
       /* stop the timeout */
-      g_source_remove (view->vertical_selection_id);
-      view->vertical_selection_id = 0;
-
-      /* get the array */
-      array = mousepad_view_vertical_selection_array (view, TRUE);
-
-      if (G_LIKELY (array != NULL))
-        {
-          /* get the buffer */
-          buffer = mousepad_view_get_buffer (view);
-
-          /* allocate the array */
-          view->marks = g_ptr_array_sized_new (array->len);
-
-          /* walk though the iters in the array */
-          for (i = 0; i < array->len; i++)
-            {
-              /* get the iter from the array */
-              iter = g_array_index (array, GtkTextIter, i);
-
-              /* create the mark */
-              mark = gtk_text_buffer_create_mark (buffer, NULL, &iter, FALSE);
+      g_source_remove (view->vertical_timeout_id);
 
-              /* append the mark to the array */
-              g_ptr_array_add (view->marks, mark);
-            }
-
-          /* free the array */
-          g_array_free (array, TRUE);
-
-          /* notify the has-selection signal */
-          buffer->has_selection = TRUE;
-          g_object_notify (G_OBJECT (buffer), "has-selection");
-        }
-
-      return TRUE;
+      /* create the marks list and emit has-selection signal */
+      if (G_LIKELY (mousepad_view_vertical_selection_finish (view)))
+        return TRUE;
     }
 
   return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->button_release_event) (widget, event);
@@ -492,143 +498,135 @@ mousepad_view_button_release_event (GtkWidget      *widget,
 /**
  * Vertical Selection Functions
  **/
-static GArray *
-mousepad_view_vertical_selection_array (MousepadView *view,
-                                        gboolean      forced)
+static void
+mousepad_view_vertical_selection_handle (MousepadView *view,
+                                         gboolean      prepend_marks)
 {
-  gint           x, y, i;
-  gint           start_y, end_y, height;
-  GArray        *array = NULL;
-  GtkTextIter    start, end;
-  GtkTextView   *text_view = GTK_TEXT_VIEW (view);
-
-  /* leave when there is no start coordinate */
-  if (G_UNLIKELY (view->vertical_start_x == -1))
-    return NULL;
+  GtkTextBuffer *buffer;
+  GtkTextView   *textview = GTK_TEXT_VIEW (view);
+  GtkTextIter    start_iter, end_iter;
+  gint           y, y_begin, y_height, y_end;
+  GtkTextMark   *start_mark, *end_mark;
 
-  /* get the cursor position */
-  gdk_window_get_pointer (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT),
-                          &x, &y, NULL);
+  /* get the buffer */
+  buffer = mousepad_view_get_buffer (view);
 
-  /* no negative values, that sucks */
-  if (G_LIKELY (x >= 0 && y >= 0))
-    {
-      /* get the buffer coordinates */
-      x += text_view->xoffset;
-      y += text_view->yoffset;
+  /* remove the old _selection_ tags */
+  gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+  gtk_text_buffer_remove_tag (buffer, view->tag, &start_iter, &end_iter);
 
-      /* quick check if we need to update the selection */
-      if (G_LIKELY (forced || view->vertical_end_x != x || view->vertical_end_y != y))
-        {
-          /* update the end coordinates */
-          view->vertical_end_x = x;
-          view->vertical_end_y = y;
+  /* get the start and end iter */
+  gtk_text_view_get_iter_at_location (textview, &start_iter, 0, view->vertical_start_y);
+  gtk_text_view_get_iter_at_location (textview, &end_iter, 0, view->vertical_end_y);
 
-          /* get the start and end iter */
-          gtk_text_view_get_iter_at_location (text_view, &start, 0, view->vertical_start_y);
-          gtk_text_view_get_iter_at_location (text_view, &end, 0, view->vertical_end_y);
+  /* make sure the start iter is before the end iter */
+  gtk_text_iter_order (&start_iter, &end_iter);
 
-          /* make sure the start iter is before the end iter */
-          gtk_text_iter_order (&start, &end);
+  /* get the y coordinates we're going to walk */
+  gtk_text_view_get_line_yrange (textview, &start_iter, &y_begin, &y_height);
+  gtk_text_view_get_line_yrange (textview, &end_iter, &y_end, NULL);
 
-          /* get the start and end location in pixels */
-          gtk_text_view_get_line_yrange (text_view, &start, &start_y, &height);
-          gtk_text_view_get_line_yrange (text_view, &end, &end_y, NULL);
+  /* make sure atleast one line is selected */
+  y_end += y_height;
 
-          /* allocate a sized array to avoid reallocation (array might be too big if there are equal iters) */
-          array = g_array_sized_new (FALSE, FALSE, sizeof (GtkTextIter), (end_y - start_y) / height * 2);
+  /* walk */
+  for (y = y_begin; y < y_end; y += y_height)
+    {
+      /* get the start and end iter in the line */
+      gtk_text_view_get_iter_at_location (textview, &start_iter, view->vertical_start_x, y);
+      gtk_text_view_get_iter_at_location (textview, &end_iter, view->vertical_end_x, y);
 
-          /* make sure atleast one line is selected */
-          end_y += height;
+      /* continue when the iters are the same */
+      if (G_UNLIKELY (gtk_text_iter_equal (&start_iter, &end_iter)))
+        continue;
 
-          /* walk through the selected lines */
-          for (i = start_y; i < end_y; i += height)
-            {
-              /* get the begin and end iter */
-              gtk_text_view_get_iter_at_location (text_view, &start, view->vertical_start_x, i);
-              gtk_text_view_get_iter_at_location (text_view, &end, view->vertical_end_x, i);
+      /* apply tag */
+      gtk_text_buffer_apply_tag (buffer, view->tag, &start_iter, &end_iter);
 
-              /* continue when the iters are the same */
-              if (gtk_text_iter_equal (&start, &end))
-                continue;
+      /* prepend to list */
+      if (G_UNLIKELY (prepend_marks))
+        {
+          /* make sure the iters are correctly sorted */
+          gtk_text_iter_order (&start_iter, &end_iter);
 
-              /* make sure the iters are in the correct order */
-              gtk_text_iter_order (&start, &end);
+          /* append start mark to the list */
+          start_mark = gtk_text_buffer_create_mark (buffer, NULL, &start_iter, FALSE);
+          view->marks = g_slist_prepend (view->marks, start_mark);
 
-              /* append the iters to the array */
-              g_array_append_val (array, start);
-              g_array_append_val (array, end);
-            }
+          /* append end mark to the list */
+          end_mark = gtk_text_buffer_create_mark (buffer, NULL, &end_iter, FALSE);
+          view->marks = g_slist_prepend (view->marks, end_mark);
         }
     }
-
-  return array;
 }
 
 
+
 static gboolean
-mousepad_view_vertical_selection_timeout (gpointer user_data)
+mousepad_view_vertical_selection_finish (MousepadView *view)
 {
-  gint           i;
-  GArray        *array;
-  GtkTextIter    start, end;
   GtkTextBuffer *buffer;
-  MousepadView  *view = MOUSEPAD_VIEW (user_data);
 
-  GDK_THREADS_ENTER ();
+  /* cleanup the marks list */
+  if (view->marks)
+    mousepad_view_vertical_selection_free_marks (view);
 
-  /* get the iters array */
-  array = mousepad_view_vertical_selection_array (view, FALSE);
+  /* update the highlight an prepend the marks */
+  mousepad_view_vertical_selection_handle (view, TRUE);
 
-  /* update the selection highlight */
-  if (array != NULL)
+  if (G_LIKELY (view->marks != NULL))
     {
+      /* reverse the list after rebuilding */
+      view->marks = g_slist_reverse (view->marks);
+
       /* get the buffer */
-      buffer = mousepad_view_get_buffer (user_data);
+      buffer = mousepad_view_get_buffer (view);
 
-      /* remove the old highlight */
-      gtk_text_buffer_get_bounds (buffer, &start, &end);
-      gtk_text_buffer_remove_tag (buffer, view->tag, &start, &end);
+      /* notify the has-selection signal */
+      buffer->has_selection = TRUE;
+      g_object_notify (G_OBJECT (buffer), "has-selection");
 
-      /* walk though the iters in the array */
-      for (i = 0; i < array->len; i += 2)
-        {
-          /* get the iters from the array */
-          start = g_array_index (array, GtkTextIter, i);
-          end   = g_array_index (array, GtkTextIter, i + 1);
+      return TRUE;
+    }
 
-          /* highlight the text between the iters */
-          gtk_text_buffer_apply_tag (buffer, view->tag, &start, &end);
-        }
+  return FALSE;
+}
 
-      /* free the array */
-      g_array_free (array, TRUE);
 
-      /* set the cursor below the pointer */
-      gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &end, view->vertical_end_x, view->vertical_end_y);
-      gtk_text_buffer_place_cursor (buffer, &end);
-    }
 
-  GDK_THREADS_LEAVE ();
+static void
+mousepad_view_vertical_selection_free_marks (MousepadView *view)
+{
+  GSList        *li;
+  GtkTextBuffer *buffer;
 
-  /* keep the timeout running */
-  return TRUE;
-}
+  /* get the buffer */
+  buffer = mousepad_view_get_buffer (view);
+
+  /* remove all the marks */
+  for (li = view->marks; li != NULL; li = li->next)
+    gtk_text_buffer_delete_mark (buffer, li->data);
+
+  /* free the list */
+  g_slist_free (view->marks);
 
+  /* null */
+  view->marks = NULL;
+}
 
 
 static void
-mousepad_view_vertical_selection_reset (MousepadView *view)
+mousepad_view_vertical_selection_clear (MousepadView *view)
 {
-  GtkTextIter    start, end;
   GtkTextBuffer *buffer;
-  gint           i;
+  GtkTextIter    start, end;
 
   _mousepad_return_if_fail (view->marks != NULL);
 
   /* get the buffer */
   buffer = mousepad_view_get_buffer (view);
 
+  /* no selection */
   buffer->has_selection = FALSE;
   g_object_notify (G_OBJECT (buffer), "has-selection");
 
@@ -640,13 +638,77 @@ mousepad_view_vertical_selection_reset (MousepadView *view)
   view->vertical_start_x = view->vertical_start_y =
   view->vertical_end_x = view->vertical_end_y = -1;
 
-  /* delete all the marks in the buffer */
-  for (i = 0; i < view->marks->len; i++)
-    gtk_text_buffer_delete_mark (buffer, g_ptr_array_index (view->marks, i));
+  /* cleanup marks */
+  mousepad_view_vertical_selection_free_marks (view);
+}
 
-  /* cleanup */
-  g_ptr_array_free (view->marks, TRUE);
-  view->marks = NULL;
+
+
+static gboolean
+mousepad_view_vertical_selection_timeout (gpointer user_data)
+{
+  MousepadView  *view = MOUSEPAD_VIEW (user_data);
+  GtkTextView   *textview = GTK_TEXT_VIEW (view);
+  gint           vertical_start_x, vertical_start_y;
+  gint           vertical_end_x, vertical_end_y;
+  GtkTextBuffer *buffer;
+  GtkTextIter    iter;
+
+  GDK_THREADS_ENTER ();
+
+  /* get the cursor coordinate */
+  gdk_window_get_pointer (gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_TEXT),
+                          &vertical_end_x, &vertical_end_y, NULL);
+
+  /* convert to positive values and buffer coordinates */
+  vertical_end_x = MAX (vertical_end_x, 0) + textview->xoffset;
+  vertical_end_y = MAX (vertical_end_y, 0) + textview->yoffset;
+
+  /* only update the selection when the cursor moved */
+  if (view->vertical_end_x != vertical_end_x || view->vertical_end_y != vertical_end_y)
+    {
+      /* update the end coordinates */
+      view->vertical_end_x = vertical_end_x;
+      view->vertical_end_y = vertical_end_y;
+
+      /* backup start coordinates */
+      vertical_start_x = view->vertical_start_x;
+      vertical_start_y = view->vertical_start_y;
+
+      /* during the timeout, we're only interested in the visible selection */
+      view->vertical_start_x = MAX (view->vertical_start_x, textview->xoffset);
+      view->vertical_start_y = MAX (view->vertical_start_y, textview->yoffset);
+
+      /* update the selection */
+      mousepad_view_vertical_selection_handle (view, FALSE);
+
+      /* restore the start coordinates */
+      view->vertical_start_x = vertical_start_x;
+      view->vertical_start_y = vertical_start_y;
+
+      /* get the text buffer */
+      buffer = mousepad_view_get_buffer (view);
+
+      /* put the cursor under the mouse pointer */
+      gtk_text_view_get_iter_at_location (textview, &iter, vertical_end_x, vertical_end_y);
+      gtk_text_buffer_place_cursor (buffer, &iter);
+
+      /* put cursor on screen */
+      mousepad_view_put_cursor_on_screen (view);
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  /* keep the timeout running */
+  return TRUE;
+}
+
+
+
+static void
+mousepad_view_vertical_selection_timeout_destroy (gpointer user_data)
+{
+  MOUSEPAD_VIEW (user_data)->vertical_timeout_id = 0;
 }
 
 
@@ -654,184 +716,202 @@ mousepad_view_vertical_selection_reset (MousepadView *view)
  * Indentation Functions
  **/
 static void
-mousepad_view_indent_line (GtkTextBuffer *buffer,
-                           GtkTextIter   *iter,
-                           gboolean       increase,
-                           gboolean       tab)
+mousepad_view_increase_indent_iter (MousepadView *view,
+                                    GtkTextIter  *iter,
+                                    gboolean      tab)
 {
-  GtkTextIter end;
-  gint        spaces;
+  gchar         *string;
+  gint           offset, length, inline_len;
+  GtkTextBuffer *buffer;
 
-  /* increase the indentation */
-  if (increase)
-    {
-      /* insert the tab or a space */
-      gtk_text_buffer_insert (buffer, iter, tab ? "\t" : " ", -1);
-    }
-  else /* decrease the indentation */
+  /* get the buffer */
+  buffer = mousepad_view_get_buffer (view);
+
+  if (view->insert_spaces && tab)
     {
-      /* set the end iter */
-      end = *iter;
+      /* get the offset */
+      offset = mousepad_util_get_real_line_offset (iter, view->tab_width);
 
-      /* remove a tab */
-      if (tab && gtk_text_iter_get_char (iter) == '\t')
-        {
-          /* get the iter after the tab */
-          gtk_text_iter_forward_char (&end);
+      /* calculate the length to inline with a tab */
+      inline_len = offset % view->tab_width;
 
-          /* remove the tab */
-          gtk_text_buffer_delete (buffer, iter, &end);
-        }
-      /* remove a space */
-      else if (gtk_text_iter_get_char (iter) == ' ')
-        {
-          /* (re)set the number of spaces */
-          spaces = tab ? 0 : 1;
+      if (inline_len == 0)
+        length = view->tab_width;
+      else
+        length = view->tab_width - inline_len;
 
-          /* count the number of spaces before the text (when unindenting tabs) */
-          while (tab && !gtk_text_iter_ends_line (&end))
-            {
-              if (gtk_text_iter_get_char (&end) == ' ')
-                spaces++;
-              else
-                break;
+      /* create spaces string */
+      string = g_strnfill (length, ' ');
 
-              gtk_text_iter_forward_char (&end);
-            }
+      /* insert string */
+      gtk_text_buffer_insert (buffer, iter, string, length);
 
-          /* delete the spaces */
-          if (spaces > 0)
-            {
-              /* reset the end iter */
-              end = *iter;
+      /* cleanup */
+      g_free (string);
+    }
+  else
+    {
+      /* insert a tab or a space */
+      gtk_text_buffer_insert (buffer, iter, tab ? "\t" : " ", -1);
+    }
+}
 
-              /* correct the number of spaces for matching tabs */
-              if (tab)
-                {
-                  /* the number of spaces we need to remove to be inlign with the tabs */
-                  spaces = spaces - (spaces / 8) * 8;
 
-                  /* we're inlign with the tabs, remove 8 spaces */
-                  if (spaces == 0)
-                    spaces = 8;
-                }
 
-              /* delete the spaces */
-              gtk_text_iter_forward_chars (&end, spaces);
-              gtk_text_buffer_delete (buffer, iter, &end);
-            }
+static void
+mousepad_view_decrease_indent_iter (MousepadView *view,
+                                    GtkTextIter  *iter,
+                                    gboolean      tab)
+{
+  GtkTextBuffer *buffer;
+  GtkTextIter    start, end;
+  gint           columns = 1;
+  gunichar       c;
+
+  /* set iters */
+  start = end = *iter;
+
+  if (gtk_text_iter_starts_line (iter))
+    {
+       /* set number of columns */
+       columns = tab ? view->tab_width : 1;
+
+       /* walk until we've removed enough columns */
+       while (columns > 0)
+         {
+           /* get the character */
+           c = gtk_text_iter_get_char (&end);
+
+           if (c == '\t')
+             columns -= view->tab_width;
+           else if (c == ' ')
+             columns--;
+           else
+             break;
+
+           /* go to the next character */
+           gtk_text_iter_forward_char (&end);
+         }
+    }
+  else
+    {
+      /* walk backwards until we've removed enough columns */
+      while (columns > 0
+             && !gtk_text_iter_starts_line (iter)
+             && gtk_text_iter_backward_char (iter))
+        {
+          /* get character */
+          c = gtk_text_iter_get_char (iter);
+
+          if (c == '\t')
+            columns -= view->tab_width;
+          else if (c == ' ')
+             columns--;
+           else
+             break;
+
+          /* update the start iter */
+          start = *iter;
         }
     }
+
+  /* unindent the selection when the iters are not equal */
+  if (!gtk_text_iter_equal (&start, &end))
+    {
+      /* get the buffer */
+      buffer = mousepad_view_get_buffer (view);
+
+      /* remove the columns */
+      gtk_text_buffer_delete (buffer, &start, &end);
+    }
 }
 
 
 
-static gboolean
-mousepad_view_indent_lines (MousepadView *view,
-                            gboolean      increase,
-                            gboolean      tab)
+static void
+mousepad_view_indent_selection (MousepadView *view,
+                                gboolean      increase,
+                                gboolean      tab)
 {
   GtkTextBuffer *buffer;
+  GtkTextIter    start_iter, end_iter;
+  GSList        *li;
   gint           start_line, end_line;
   gint           i;
-  GtkTextIter    start, end, needle;
-  GtkTextMark   *mark;
-  gboolean       succeed = FALSE;
 
   /* get the textview buffer */
   buffer = mousepad_view_get_buffer (view);
 
-  /* if we have a vertical selection, we're not handling that here */
-  if (view->vertical_start_x != -1)
+  /* begin a user action */
+  gtk_text_buffer_begin_user_action (buffer);
+
+  if (view->marks != NULL)
     {
-      /* walk through the start marks (hence the +2) */
-      for (i = 0; i < view->marks->len; i += 2)
+      for (li = view->marks; li != NULL; li = li->next)
         {
-          /* get the mark */
-          mark = g_ptr_array_index (view->marks, i);
-
-          /* get the iter at this mark */
-          gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
+          /* get the iter */
+          gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
 
-          /* first move the iter when we're going to indent */
-          if (!increase)
+          /* only indent when the iter does not start a line */
+          if (!gtk_text_iter_starts_line (&start_iter))
             {
-              /* sync the iters */
-              needle = end = start;
-
-              /* walk backwards till we hit text */
-              while (gtk_text_iter_backward_char (&needle)
-                     && !gtk_text_iter_starts_line (&needle))
-                {
-                  if ((gtk_text_iter_get_char (&needle) == ' ' && !tab)
-                      || (gtk_text_iter_get_char (&needle) == '\t' && tab))
-                    end = needle;
-                  else
-                    break;
-                }
-
-              /* continue if the old and new iter are the same, this makes sure
-               * we don't indent spaces in the block */
-              if (gtk_text_iter_equal (&start, &end))
-                continue;
-
-              /* set the new start iter */
-              start = end;
+              /* increase or decrease the indentation */
+              if (increase)
+                mousepad_view_increase_indent_iter (view, &start_iter, tab);
+              else
+                mousepad_view_decrease_indent_iter (view, &start_iter, tab);
             }
 
-          /* (de/in)crease the line the line with a tab or a space */
-          mousepad_view_indent_line (buffer, &start, increase, tab);
+          /* skip end mark in the list (always pairs of two) */
+          li = li->next;
         }
-
-      succeed = TRUE;
     }
-
-  if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
+  else if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
     {
-      /* only indent when the entire line or multiple lines are selected */
-      if (increase
-          && !(gtk_text_iter_starts_line (&start) && gtk_text_iter_ends_line (&end))
-          && (gtk_text_iter_get_line (&start) == gtk_text_iter_get_line (&end)))
-        goto failed;
-
-      /* get the line numbers for the start and end iter */
-      start_line = gtk_text_iter_get_line (&start);
-      end_line = gtk_text_iter_get_line (&end);
-
-      /* insert a tab in front of each line */
-      for (i = start_line; i <= end_line; i++)
+      /* get start and end line */
+      start_line = gtk_text_iter_get_line (&start_iter);
+      end_line = gtk_text_iter_get_line (&end_iter);
+
+      /* only change indentation when an entire line is selected or multiple lines */
+      if (start_line != end_line
+          || (gtk_text_iter_starts_line (&start_iter)
+              && gtk_text_iter_ends_line (&end_iter)))
         {
-          /* get the iter of the line we're going to indent */
-          gtk_text_buffer_get_iter_at_line (buffer, &start, i);
+          /* change indentation of each line */
+          for (i = start_line; i <= end_line; i++)
+            {
+              /* get the iter of the line we're going to indent */
+              gtk_text_buffer_get_iter_at_line (buffer, &start_iter, i);
 
-          /* don't (un)indent empty lines */
-          if (gtk_text_iter_ends_line (&start))
-            continue;
+              /* don't change indentation of empty lines */
+              if (gtk_text_iter_ends_line (&start_iter))
+                continue;
 
-          /* change the indentation of this line */
-          mousepad_view_indent_line (buffer, &start, increase, tab);
+              /* increase or decrease the indentation */
+              if (increase)
+                mousepad_view_increase_indent_iter (view, &start_iter, tab);
+              else
+                mousepad_view_decrease_indent_iter (view, &start_iter, tab);
+            }
         }
-
-      succeed = TRUE;
     }
 
-failed:
-  /* scroll */
-  gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
-                                      gtk_text_buffer_get_insert (buffer));
+  /* end user action */
+  gtk_text_buffer_end_user_action (buffer);
 
-  return succeed;
+  /* put cursor on screen */
+  mousepad_view_put_cursor_on_screen (view);
 }
 
 
 
 static gchar *
-mousepad_view_compute_indentation (GtkTextBuffer     *buffer,
-                                   const GtkTextIter *iter)
+mousepad_view_indentation_string (GtkTextBuffer     *buffer,
+                                  const GtkTextIter *iter)
 {
   GtkTextIter start, end;
   gint        line;
-  gunichar    ch;
+  gunichar    c;
 
   /* get the line of the iter */
   line = gtk_text_iter_get_line (iter);
@@ -842,22 +922,17 @@ mousepad_view_compute_indentation (GtkTextBuffer     *buffer,
   /* set the end iter */
   end = start;
 
-  /* get the first character */
-  ch = gtk_text_iter_get_char (&end);
-
-  /* keep walking till we hit text or a new line */
-  while (g_unichar_isspace (ch)
-         && ch != '\n'
-         && ch != '\r'
-         && gtk_text_iter_compare (&end, iter) < 0)
+  /* forward to text */
+  do
     {
-      /* break if we can't step forward */
-      if (!gtk_text_iter_forward_char (&end))
-        break;
+      /* get the iter character */
+      c = gtk_text_iter_get_char (&end);
 
-      /* get the next character */
-      ch = gtk_text_iter_get_char (&end);
+      /* break if the character is not a space or equal to the iter */
+      if (!g_unichar_isspace (c) || gtk_text_iter_equal (&end, iter))
+        break;
     }
+  while (gtk_text_iter_forward_char (&end));
 
   /* return NULL if the iters are the same */
   if (gtk_text_iter_equal (&start, &end))
@@ -869,14 +944,49 @@ mousepad_view_compute_indentation (GtkTextBuffer     *buffer,
 
 
 
+static gint
+mousepad_view_calculate_layout_width (GtkWidget *widget,
+                                      gsize      length,
+                                      gchar      fill_char)
+{
+  PangoLayout *layout;
+  gchar       *string;
+  gint         width = -1;
+
+  _mousepad_return_val_if_fail (GTK_IS_WIDGET (widget), -1);
+  _mousepad_return_val_if_fail (length > 0, -1);
+
+  /* create character string */
+  string = g_strnfill (length, fill_char);
+
+  /* create pango layout from widget */
+  layout = gtk_widget_create_pango_layout (widget, string);
+
+  /* cleanup */
+  g_free (string);
+
+  if (G_LIKELY (layout))
+    {
+      /* get the layout size */
+      pango_layout_get_pixel_size (layout, &width, NULL);
+
+      /* release layout */
+      g_object_unref (G_OBJECT (layout));
+    }
+
+  return width;
+}
+
+
+
 static void
-mousepad_view_handle_clipboard (MousepadView *view,
-                                GtkClipboard *clipboard,
-                                gboolean      remove)
+mousepad_view_clipboard (MousepadView *view,
+                         GtkClipboard *clipboard,
+                         gboolean      remove)
 {
   GString       *string = NULL;
-  gint           i;
-  gint           ln, previous_ln = 0;
+  GSList        *li;
+  gint           ln, previous_ln = -1;
   gchar         *slice;
   GtkTextBuffer *buffer;
   GtkTextMark   *mark_start, *mark_end;
@@ -886,28 +996,36 @@ mousepad_view_handle_clipboard (MousepadView *view,
   buffer = mousepad_view_get_buffer (view);
 
   /* create a new string */
-  if (G_LIKELY (clipboard))
+  if (clipboard)
     string = g_string_new (NULL);
 
+  /* begin user action */
+  gtk_text_buffer_begin_user_action (buffer);
+
   /* walk through the marks */
-  for (i = 0; i < view->marks->len; i += 2)
+  for (li = view->marks; li != NULL; li = li->next)
     {
-      /* get the marks */
-      mark_start = g_ptr_array_index (view->marks, i);
-      mark_end   = g_ptr_array_index (view->marks, i + 1);
+      /* get the start mark */
+      mark_start = li->data;
+
+      /* jump to next item in the list */
+      li = li->next;
+
+      /* get end mark */
+      mark_end = li->data;
 
       /* and their iter positions */
       gtk_text_buffer_get_iter_at_mark (buffer, &iter_start, mark_start);
       gtk_text_buffer_get_iter_at_mark (buffer, &iter_end, mark_end);
 
       /* only handle the selection when there is clipbaord */
-      if (G_LIKELY (clipboard))
+      if (clipboard)
         {
           /* the line number of the iter */
           ln = gtk_text_iter_get_line (&iter_start);
 
           /* fix the number of new lines between the parts */
-          if (i == 0)
+          if (previous_ln == -1)
             previous_ln = ln;
           else
             for (; previous_ln < ln; previous_ln++)
@@ -928,6 +1046,9 @@ mousepad_view_handle_clipboard (MousepadView *view,
         gtk_text_buffer_delete (buffer, &iter_start, &iter_end);
     }
 
+  /* end user action */
+  gtk_text_buffer_end_user_action (buffer);
+
   /* set the clipboard text */
   if (G_LIKELY (clipboard))
     {
@@ -941,111 +1062,178 @@ mousepad_view_handle_clipboard (MousepadView *view,
 
 
 
-gboolean
-mousepad_view_get_vertical_selection (MousepadView *view)
+void
+mousepad_view_put_cursor_on_screen (MousepadView *view)
 {
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+  GtkTextBuffer *buffer;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
 
-  return (view->marks != NULL);
+  /* get buffer */
+  buffer = mousepad_view_get_buffer (view);
+
+  /* scroll to visible area */
+  gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
+                                gtk_text_buffer_get_insert (buffer),
+                                0.02, FALSE, 0.0, 0.0);
 }
 
 
 
 void
-mousepad_view_cut_clipboard (MousepadView *view,
-                             GtkClipboard *clipboard)
+mousepad_view_clipboard_cut (MousepadView *view)
 {
+  GtkClipboard  *clipboard;
+  GtkTextBuffer *buffer;
+
   _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
-  _mousepad_return_if_fail (view->marks != NULL);
-  _mousepad_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
+  _mousepad_return_if_fail (mousepad_view_get_has_selection (view));
+
+  /* get the clipboard */
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
+
+  if (view->marks)
+    {
+      /* call the clipboard function with remove bool */
+      mousepad_view_clipboard (view, clipboard, TRUE);
 
-  /* call the clipboard function with remove bool */
-  mousepad_view_handle_clipboard (view, clipboard, TRUE);
+      /* cleanup the vertical selection */
+      mousepad_view_vertical_selection_clear (view);
+    }
+  else
+    {
+      /* get buffer */
+      buffer = mousepad_view_get_buffer (view);
+
+      /* cut from buffer */
+      gtk_text_buffer_cut_clipboard (buffer, clipboard, gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
+    }
 
-  /* cleanup the vertical selection */
-  mousepad_view_vertical_selection_reset (view);
+  /* put cursor on screen */
+  mousepad_view_put_cursor_on_screen (view);
 }
 
 
 
 void
-mousepad_view_copy_clipboard (MousepadView *view,
-                              GtkClipboard *clipboard)
+mousepad_view_clipboard_copy (MousepadView *view)
 {
+  GtkClipboard  *clipboard;
+  GtkTextBuffer *buffer;
+
   _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
-  _mousepad_return_if_fail (view->marks != NULL);
-  _mousepad_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
+  _mousepad_return_if_fail (mousepad_view_get_has_selection (view));
+
+  /* get the clipboard */
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
+
+  if (view->marks)
+    {
+      /* call the clipboard function */
+      mousepad_view_clipboard (view, clipboard, FALSE);
+    }
+  else
+    {
+      /* get buffer */
+      buffer = mousepad_view_get_buffer (view);
 
-  /* call the clipboard function without remove bool */
-  mousepad_view_handle_clipboard (view, clipboard, FALSE);
+      /* copy from buffer */
+      gtk_text_buffer_copy_clipboard (buffer, clipboard);
+    }
+
+  /* put cursor on screen */
+  mousepad_view_put_cursor_on_screen (view);
 }
 
 
 
 void
-mousepad_view_paste_column_clipboard (MousepadView *view,
-                                      GtkClipboard *clipboard)
+mousepad_view_clipboard_paste (MousepadView *view,
+                               gboolean      paste_as_column)
 {
-  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
-  _mousepad_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
-
-  GtkTextMark   *mark;
-  GtkTextIter    iter;
-  GtkTextBuffer *buffer;
-  GtkTextView   *text_view;
-  gchar         *string, **pieces;
-  gint           i, y;
-  GdkRectangle   rect;
+  GtkClipboard   *clipboard;
+  GtkTextBuffer  *buffer;
+  GtkTextView    *textview = GTK_TEXT_VIEW (view);
+  gchar          *string;
+  GtkTextMark    *mark;
+  GtkTextIter     iter;
+  GdkRectangle    rect;
+  gchar         **pieces;
+  gint            i, y;
+
+  /* get the clipboard */
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
 
-  /* get the text buffer */
+  /* get the buffer */
   buffer = mousepad_view_get_buffer (view);
-  text_view = GTK_TEXT_VIEW (view);
 
-  /* get the clipboard text */
-  string = gtk_clipboard_wait_for_text (clipboard);
-
-  if (G_LIKELY (string != NULL))
+  if (paste_as_column)
     {
-      /* chop the string into pieces */
-      pieces = g_strsplit (string, "\n", -1);
+      /* get the clipboard text */
+      string = gtk_clipboard_wait_for_text (clipboard);
 
-      /* cleanup */
-      g_free (string);
+      if (G_LIKELY (string))
+        {
+          /* chop the string into pieces */
+          pieces = g_strsplit (string, "\n", -1);
 
-      /* get the iter are the cursor position */
-      mark = gtk_text_buffer_get_insert (buffer);
-      gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+          /* cleanup */
+          g_free (string);
 
-      /* get the buffer location */
-      gtk_text_view_get_iter_location (text_view, &iter, &rect);
+          /* get iter at cursor position */
+          mark = gtk_text_buffer_get_insert (buffer);
+          gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
 
-      /* insert the pieces in the buffer */
-      for (i = 0; pieces[i] != NULL; i++)
-        {
-          /* insert the text in the buffer */
-          gtk_text_buffer_insert (buffer, &iter, pieces[i], -1);
+          /* get the iter location */
+          gtk_text_view_get_iter_location (textview, &iter, &rect);
 
-          /* don't try to add a new line if we reached the end of the array */
-          if (G_UNLIKELY (pieces[i+1] == NULL))
-            break;
+          /* begin user action */
+          gtk_text_buffer_begin_user_action (buffer);
 
-          /* move the iter to the next line and add a new line if needed */
-          if (!gtk_text_iter_forward_line (&iter))
-             gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+          /* insert the pieces in the buffer */
+          for (i = 0; pieces[i] != NULL; i++)
+            {
+              /* insert the text in the buffer */
+              gtk_text_buffer_insert (buffer, &iter, pieces[i], -1);
 
-          /* get the y coordinate for this line */
-          gtk_text_view_get_line_yrange (text_view, &iter, &y, NULL);
+              /* break if the next piece is null */
+              if (G_UNLIKELY (pieces[i+1] == NULL))
+                break;
 
-          /* get the iter at the y location of this line and the same x as the cursor */
-          gtk_text_view_get_iter_at_location (text_view, &iter, rect.x, y);
-        }
+              /* move the iter to the next line */
+              if (!gtk_text_iter_forward_line (&iter))
+                {
+                  /* no new line, insert a new line */
+                  gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+                }
+              else
+                {
+                  /* get the y coordinate for this line */
+                  gtk_text_view_get_line_yrange (textview, &iter, &y, NULL);
 
-      /* cleanup */
-      g_strfreev (pieces);
+                  /* get the iter at the correct coordinate */
+                  gtk_text_view_get_iter_at_location (textview, &iter, rect.x, y);
+                }
+            }
 
-      /* set the cursor at the last iter position */
-      gtk_text_buffer_place_cursor (buffer, &iter);
+          /* cleanup */
+          g_strfreev (pieces);
+
+          /* set the cursor to the last iter position */
+          gtk_text_buffer_place_cursor (buffer, &iter);
+
+          /* end user action */
+          gtk_text_buffer_end_user_action (buffer);
+        }
+    }
+  else
+    {
+      /* paste as normal string */
+      gtk_text_buffer_paste_clipboard (buffer, clipboard, NULL, gtk_text_view_get_editable (textview));
     }
+
+  /* put cursor on screen */
+  mousepad_view_put_cursor_on_screen (view);
 }
 
 
@@ -1053,33 +1241,73 @@ mousepad_view_paste_column_clipboard (MousepadView *view,
 void
 mousepad_view_delete_selection (MousepadView *view)
 {
+  GtkTextBuffer *buffer;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+  _mousepad_return_if_fail (mousepad_view_get_has_selection (view));
+
+  if (view->marks)
+    {
+      /* remove the text in the vertical selection */
+      mousepad_view_clipboard (view, NULL, TRUE);
+
+      /* cleanup the vertical selection */
+      mousepad_view_vertical_selection_clear (view);
+    }
+  else
+    {
+      /* get the buffer */
+      buffer = mousepad_view_get_buffer (view);
+
+      /* delete the selection */
+      gtk_text_buffer_delete_selection (buffer, TRUE, gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
+    }
+
+  /* put cursor on screen */
+  mousepad_view_put_cursor_on_screen (view);
+}
+
+
+
+void
+mousepad_view_select_all (MousepadView *view)
+{
+  GtkTextIter    start, end;
+  GtkTextBuffer *buffer;
+
   _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
-  _mousepad_return_if_fail (view->marks != NULL);
 
-  /* set the clipbaord to NULL to remove the selection */
-  mousepad_view_handle_clipboard (view, NULL, TRUE);
+  /* cleanup vertical selection */
+  if (view->marks)
+    mousepad_view_vertical_selection_clear (view);
 
-  /* cleanup the vertical selection */
-  mousepad_view_vertical_selection_reset (view);
+  /* get buffer */
+  buffer = mousepad_view_get_buffer (view);
+
+  /* get the start and end iter */
+  gtk_text_buffer_get_bounds (buffer, &start, &end);
+
+  /* select everything between those iters */
+  gtk_text_buffer_select_range (buffer, &start, &end);
 }
 
 
 
 void
-mousepad_view_set_show_line_numbers (MousepadView *view,
-                                     gboolean      visible)
+mousepad_view_set_line_numbers (MousepadView *view,
+                                gboolean      line_numbers)
 {
   _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
 
-  if (view->line_numbers != visible)
+  if (view->line_numbers != line_numbers)
     {
       /* set the boolean */
-      view->line_numbers = visible;
+      view->line_numbers = line_numbers;
 
       /* set the left border size */
       gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view),
                                             GTK_TEXT_WINDOW_LEFT,
-                                            visible ? 20 : 0);
+                                            line_numbers ? 20 : 0);
 
       /* make sure there is an expose event */
       gtk_widget_queue_draw (GTK_WIDGET (view));
@@ -1090,10 +1318,114 @@ mousepad_view_set_show_line_numbers (MousepadView *view,
 
 void
 mousepad_view_set_auto_indent (MousepadView *view,
-                               gboolean      enable)
+                               gboolean      auto_indent)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+  /* set the boolean */
+  view->auto_indent = auto_indent;
+}
+
+
+
+void
+mousepad_view_set_tab_width (MousepadView *view,
+                             gint          tab_width)
+{
+  PangoTabArray *tab_array;
+  gint           layout_width;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+  _mousepad_return_if_fail (GTK_IS_TEXT_VIEW (view));
+
+  /* set the value */
+  view->tab_width = tab_width;
+
+  /* get the pixel width of the tab size */
+  layout_width = mousepad_view_calculate_layout_width (GTK_WIDGET (view), view->tab_width, ' ');
+
+  if (G_LIKELY (layout_width != -1))
+    {
+      /* create the pango tab array */
+      tab_array = pango_tab_array_new (1, TRUE);
+      pango_tab_array_set_tab (tab_array, 0, PANGO_TAB_LEFT, layout_width);
+
+      /* set the textview tab array */
+      gtk_text_view_set_tabs (GTK_TEXT_VIEW (view), tab_array);
+
+      /* cleanup */
+      pango_tab_array_free (tab_array);
+    }
+}
+
+
+
+void
+mousepad_view_set_insert_spaces (MousepadView *view,
+                                 gboolean      insert_spaces)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+  /* set boolean */
+  view->insert_spaces = insert_spaces;
+}
+
+
+
+gboolean
+mousepad_view_get_has_selection (MousepadView *view)
+{
+  GtkTextBuffer *buffer;
+
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+  /* we have a vertical selection */
+  if (view->marks != NULL)
+    return TRUE;
+
+  /* get the text buffer */
+  buffer = mousepad_view_get_buffer (view);
+
+  /* normal selection */
+  return gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL);
+}
+
+
+
+gboolean
+mousepad_view_get_line_numbers (MousepadView *view)
 {
-   _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+  return view->line_numbers;
+}
+
+
+
+gboolean
+mousepad_view_get_auto_indent (MousepadView *view)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+  return view->auto_indent;
+}
+
+
+
+gint
+mousepad_view_get_tab_width (MousepadView *view)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), -1);
+
+  return view->tab_width;
+}
+
+
+
+gboolean
+mousepad_view_get_insert_spaces (MousepadView *view)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
 
-   /* set the boolean */
-   view->auto_indent = enable;
+  return view->insert_spaces;
 }
diff --git a/mousepad/mousepad-view.h b/mousepad/mousepad-view.h
index 92da429..eef1d5a 100644
--- a/mousepad/mousepad-view.h
+++ b/mousepad/mousepad-view.h
@@ -32,24 +32,40 @@ typedef struct _MousepadView      MousepadView;
 
 GType           mousepad_view_get_type                  (void) G_GNUC_CONST;
 
-gboolean        mousepad_view_get_vertical_selection    (MousepadView *view);
+void            mousepad_view_put_cursor_on_screen      (MousepadView      *view);
 
-void            mousepad_view_cut_clipboard             (MousepadView *view,
-                                                         GtkClipboard *clipboard);
+void            mousepad_view_clipboard_cut             (MousepadView      *view);
 
-void            mousepad_view_copy_clipboard            (MousepadView *view,
-                                                         GtkClipboard *clipboard);
+void            mousepad_view_clipboard_copy            (MousepadView      *view);
 
-void            mousepad_view_paste_column_clipboard    (MousepadView *view,
-                                                         GtkClipboard *clipboard);
+void            mousepad_view_clipboard_paste           (MousepadView      *view,
+                                                         gboolean           paste_as_column);
 
-void            mousepad_view_delete_selection          (MousepadView *view);
+void            mousepad_view_delete_selection          (MousepadView      *view);
 
-void            mousepad_view_set_show_line_numbers     (MousepadView *view,
-                                                         gboolean      visible);
+void            mousepad_view_select_all                (MousepadView      *view);
 
-void            mousepad_view_set_auto_indent           (MousepadView *view,
-                                                         gboolean      enable);
+void            mousepad_view_set_line_numbers          (MousepadView      *view,
+                                                         gboolean           line_numbers);
+
+void            mousepad_view_set_auto_indent           (MousepadView      *view,
+                                                         gboolean           auto_indent);
+
+void            mousepad_view_set_tab_width             (MousepadView      *view,
+                                                         gint               tab_width);
+
+void            mousepad_view_set_insert_spaces         (MousepadView      *view,
+                                                         gboolean           insert_spaces);
+
+gboolean        mousepad_view_get_has_selection         (MousepadView      *view);
+
+gboolean        mousepad_view_get_line_numbers          (MousepadView      *view);
+
+gboolean        mousepad_view_get_auto_indent           (MousepadView      *view);
+
+gint            mousepad_view_get_tab_width             (MousepadView      *view);
+
+gboolean        mousepad_view_get_insert_spaces         (MousepadView      *view);
 
 G_END_DECLS
 
diff --git a/mousepad/mousepad-window-ui.xml b/mousepad/mousepad-window-ui.xml
index 6df88b6..4304bab 100644
--- a/mousepad/mousepad-window-ui.xml
+++ b/mousepad/mousepad-window-ui.xml
@@ -77,6 +77,7 @@
       <menuitem action="word-wrap" />
       <menuitem action="line-numbers" />
       <menuitem action="auto-indent" />
+      <menuitem action="insert-spaces" />
     </menu>
 
     <menu action="go-menu">
diff --git a/mousepad/mousepad-window.c b/mousepad/mousepad-window.c
index c9da3cc..40246dc 100644
--- a/mousepad/mousepad-window.c
+++ b/mousepad/mousepad-window.c
@@ -32,13 +32,16 @@
 #include <glib/gstdio.h>
 
 #include <mousepad/mousepad-private.h>
-#include <mousepad/mousepad-types.h>
 #include <mousepad/mousepad-application.h>
+#include <mousepad/mousepad-marshal.h>
 #include <mousepad/mousepad-document.h>
 #include <mousepad/mousepad-dialogs.h>
 #include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-replace-dialog.h>
+#include <mousepad/mousepad-encoding-dialog.h>
 #include <mousepad/mousepad-search-bar.h>
 #include <mousepad/mousepad-statusbar.h>
+#include <mousepad/mousepad-print.h>
 #include <mousepad/mousepad-window.h>
 #include <mousepad/mousepad-window-ui.h>
 
@@ -46,12 +49,17 @@
 
 #define WINDOW_SPACING 3
 
+#if GTK_CHECK_VERSION (2,12,0)
+static gpointer NOTEBOOK_GROUP = "Mousepad";
+#endif
+
 
 
 enum
 {
   NEW_WINDOW,
   NEW_WINDOW_WITH_DOCUMENT,
+  HAS_DOCUMENTS,
   LAST_SIGNAL,
 };
 
@@ -84,33 +92,29 @@ static gboolean          mousepad_window_save_geometry_timer          (gpointer
 static void              mousepad_window_save_geometry_timer_destroy  (gpointer                user_data);
 
 /* window functions */
-static gboolean          mousepad_window_save                         (MousepadWindow         *window,
-                                                                       MousepadDocument       *document,
-                                                                       gboolean                force_save_as);
 static gboolean          mousepad_window_close_document               (MousepadWindow         *window,
                                                                        MousepadDocument       *document);
-static void              mousepad_window_set_title                    (MousepadWindow         *window,
-                                                                       MousepadDocument       *document);
+static void              mousepad_window_set_title                    (MousepadWindow         *window);
 static void              mousepad_window_toggle_overwrite             (MousepadWindow         *window,
                                                                        gboolean                overwrite);
 
 /* notebook signals */
-static void              mousepad_window_page_notified                (GtkNotebook            *notebook,
+static void              mousepad_window_notebook_notified            (GtkNotebook            *notebook,
                                                                        GParamSpec             *pspec,
                                                                        MousepadWindow         *window);
-static void              mousepad_window_page_reordered               (GtkNotebook            *notebook,
+static void              mousepad_window_notebook_reordered           (GtkNotebook            *notebook,
                                                                        GtkWidget              *page,
                                                                        guint                   page_num,
                                                                        MousepadWindow         *window);
-static void              mousepad_window_page_added                   (GtkNotebook            *notebook,
+static void              mousepad_window_notebook_added               (GtkNotebook            *notebook,
                                                                        GtkWidget              *page,
                                                                        guint                   page_num,
                                                                        MousepadWindow         *window);
-static void              mousepad_window_page_removed                 (GtkNotebook            *notebook,
+static void              mousepad_window_notebook_removed             (GtkNotebook            *notebook,
                                                                        GtkWidget              *page,
                                                                        guint                   page_num,
                                                                        MousepadWindow         *window);
-static void              mousepad_window_tab_popup_position           (GtkMenu                *menu,
+static void              mousepad_window_notebook_menu_position       (GtkMenu                *menu,
                                                                        gint                   *x,
                                                                        gint                   *y,
                                                                        gboolean               *push_in,
@@ -118,10 +122,16 @@ static void              mousepad_window_tab_popup_position           (GtkMenu
 static gboolean          mousepad_window_notebook_button_press_event  (GtkNotebook            *notebook,
                                                                        GdkEventButton         *event,
                                                                        MousepadWindow         *window);
+#if GTK_CHECK_VERSION (2,12,0)
+static GtkNotebook      *mousepad_window_notebook_create_window       (GtkNotebook            *notebook,
+                                                                       GtkWidget              *page,
+                                                                       gint                    x,
+                                                                       gint                    y,
+                                                                       MousepadWindow         *window);
+#endif
 
 /* document signals */
-static void              mousepad_window_modified_changed             (MousepadDocument       *document,
-                                                                       MousepadWindow         *window);
+static void              mousepad_window_modified_changed             (MousepadWindow         *window);
 static void              mousepad_window_cursor_changed               (MousepadDocument       *document,
                                                                        guint                   line,
                                                                        guint                   column,
@@ -139,15 +149,13 @@ static void              mousepad_window_can_redo                     (MousepadW
 
 /* menu updaters */
 static void              mousepad_window_update_actions               (MousepadWindow         *window);
-static void              mousepad_window_update_sensitivity           (MousepadWindow         *window,
-                                                                       gboolean                sensitive);
 static gboolean          mousepad_window_update_gomenu_idle           (gpointer                user_data);
 static void              mousepad_window_update_gomenu_idle_destroy   (gpointer                user_data);
 static void              mousepad_window_update_gomenu                (MousepadWindow         *window);
 
 /* recent functions */
 static void              mousepad_window_recent_add                   (MousepadWindow         *window,
-                                                                       const gchar            *filename);
+                                                                       MousepadFile           *file);
 static gchar            *mousepad_window_recent_escape_underscores    (const gchar            *str);
 static gint              mousepad_window_recent_sort                  (GtkRecentInfo          *a,
                                                                        GtkRecentInfo          *b);
@@ -168,12 +176,6 @@ static void              mousepad_window_drag_data_received           (GtkWidget
 
 /* search bar */
 static void              mousepad_window_hide_search_bar              (MousepadWindow         *window);
-static gboolean          mousepad_window_find_string                  (MousepadWindow         *window,
-                                                                       const gchar            *string,
-                                                                       MousepadSearchFlags     flags);
-static gboolean          mousepad_window_highlight_all                (MousepadWindow         *window,
-                                                                       const gchar            *string,
-                                                                       MousepadSearchFlags     flags);
 
 /* actions */
 static void              mousepad_window_action_open_new_tab          (GtkAction              *action,
@@ -186,9 +188,9 @@ static void              mousepad_window_action_open_recent           (GtkAction
                                                                        MousepadWindow         *window);
 static void              mousepad_window_action_clear_recent          (GtkAction              *action,
                                                                        MousepadWindow         *window);
-static void              mousepad_window_action_save_file             (GtkAction              *action,
+static gboolean          mousepad_window_action_save_file             (GtkAction              *action,
                                                                        MousepadWindow         *window);
-static void              mousepad_window_action_save_file_as          (GtkAction              *action,
+static gboolean          mousepad_window_action_save_file_as          (GtkAction              *action,
                                                                        MousepadWindow         *window);
 static void              mousepad_window_action_reload                (GtkAction              *action,
                                                                        MousepadWindow         *window);
@@ -236,6 +238,8 @@ static void              mousepad_window_action_line_numbers          (GtkToggle
                                                                        MousepadWindow         *window);
 static void              mousepad_window_action_auto_indent           (GtkToggleAction        *action,
                                                                        MousepadWindow         *window);
+static void              mousepad_window_action_insert_spaces         (GtkToggleAction        *action,
+                                                                       MousepadWindow         *window);
 static void              mousepad_window_action_prev_tab              (GtkAction              *action,
                                                                        MousepadWindow         *window);
 static void              mousepad_window_action_next_tab              (GtkAction              *action,
@@ -292,6 +296,7 @@ struct _MousepadWindow
   GtkWidget           *container;
   GtkWidget           *search_bar;
   GtkWidget           *statusbar;
+  GtkWidget           *replace_dialog;
 
   /* support to remember window geometry */
   guint                save_geometry_timer_id;
@@ -306,7 +311,7 @@ struct _MousepadWindow
 static const GtkActionEntry action_entries[] =
 {
   { "file-menu", NULL, N_("_File"), NULL, NULL, NULL, },
-    { "new-tab", "tab-new", N_("New _Tab"), "<control>T", N_("Create a new document"), G_CALLBACK (mousepad_window_action_open_new_tab), },
+    { "new-tab", "tab-new", N_("New Documen_t"), "<control>T", N_("Create a new document"), G_CALLBACK (mousepad_window_action_open_new_tab), },
     { "new-window", "window-new", N_("_New Window"), "<control>N", N_("Create a new document in a new window"), G_CALLBACK (mousepad_window_action_open_new_window), },
     { "open-file", GTK_STOCK_OPEN, N_("_Open File"), NULL, N_("Open a file"), G_CALLBACK (mousepad_window_action_open_file), },
     { "recent-menu", NULL, N_("Open _Recent"), NULL, NULL, NULL, },
@@ -354,8 +359,9 @@ static const GtkToggleActionEntry toggle_action_entries[] =
 {
   { "statusbar", NULL, N_("_Statusbar"), NULL, N_("Change the visibility of the statusbar"), G_CALLBACK (mousepad_window_action_statusbar), FALSE, },
   { "word-wrap", NULL, N_("_Word Wrap"), NULL, N_("Toggle breaking lines in between words"), G_CALLBACK (mousepad_window_action_word_wrap), FALSE, },
-  { "line-numbers", NULL, N_("_Line Numbers"), NULL, NULL, G_CALLBACK (mousepad_window_action_line_numbers), FALSE, },
-  { "auto-indent", NULL, N_("_Auto Indent"), NULL, NULL, G_CALLBACK (mousepad_window_action_auto_indent), FALSE, },
+  { "line-numbers", NULL, N_("_Line Numbers"), NULL, N_("Show line numbers"), G_CALLBACK (mousepad_window_action_line_numbers), FALSE, },
+  { "auto-indent", NULL, N_("_Auto Indent"), NULL, N_("Auto indent a new line"), G_CALLBACK (mousepad_window_action_auto_indent), FALSE, },
+  { "insert-spaces", NULL, N_("_Insert Spaces"), NULL, N_("Insert spaces when the tab button is pressed"), G_CALLBACK (mousepad_window_action_insert_spaces), FALSE, },
 };
 
 
@@ -412,7 +418,7 @@ mousepad_window_class_init (MousepadWindowClass *klass)
   window_signals[NEW_WINDOW] =
     g_signal_new (I_("new-window"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
                   g_cclosure_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
@@ -420,11 +426,21 @@ mousepad_window_class_init (MousepadWindowClass *klass)
   window_signals[NEW_WINDOW_WITH_DOCUMENT] =
     g_signal_new (I_("new-window-with-document"),
                   G_TYPE_FROM_CLASS (gobject_class),
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_NO_HOOKS,
                   0, NULL, NULL,
-                  g_cclosure_marshal_VOID__OBJECT,
+                  _mousepad_marshal_VOID__OBJECT_INT_INT,
+                  G_TYPE_NONE, 3,
+                  G_TYPE_OBJECT,
+                  G_TYPE_INT, G_TYPE_INT);
+
+  window_signals[HAS_DOCUMENTS] =
+    g_signal_new (I_("has-documents"),
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_NO_HOOKS,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__BOOLEAN,
                   G_TYPE_NONE, 1,
-                  G_TYPE_OBJECT);
+                  G_TYPE_BOOLEAN);
 }
 
 
@@ -439,7 +455,7 @@ mousepad_window_init (MousepadWindow *window)
   GtkWidget     *ebox;
   GtkAction     *action;
   gint           width, height;
-  gboolean       visible;
+  gboolean       statusbar_visible;
 
   /* initialize stuff */
   window->save_geometry_timer_id = 0;
@@ -449,6 +465,7 @@ mousepad_window_init (MousepadWindow *window)
   window->recent_merge_id = 0;
   window->search_bar = NULL;
   window->statusbar = NULL;
+  window->replace_dialog = NULL;
   window->active = NULL;
 
   /* add the preferences to the window */
@@ -459,19 +476,19 @@ mousepad_window_init (MousepadWindow *window)
   g_closure_ref (window->menu_item_selected_closure);
   g_closure_sink (window->menu_item_selected_closure);
 
+  /* signal for handling the window delete event */
+  g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (mousepad_window_delete_event), NULL);
+
   /* allocate a closure for the menu_item_deselected() callback */
   window->menu_item_deselected_closure = g_cclosure_new_object (G_CALLBACK (mousepad_window_menu_item_deselected), G_OBJECT (window));
   g_closure_ref (window->menu_item_deselected_closure);
   g_closure_sink (window->menu_item_deselected_closure);
 
-  /* signal for handling the window delete event */
-  g_signal_connect (G_OBJECT (window), "delete-event",
-                    G_CALLBACK (mousepad_window_delete_event), NULL);
-
   /* read settings from the preferences */
   g_object_get (G_OBJECT (window->preferences),
-                "last-window-width", &width,
-                "last-window-height", &height,
+                "window-width", &width,
+                "window-height", &height,
+                "window-statusbar-visible", &statusbar_visible,
                 NULL);
 
   /* set the default window size */
@@ -554,15 +571,24 @@ mousepad_window_init (MousepadWindow *window)
                                    "show-tabs", FALSE,
                                    "tab-hborder", 0,
                                    "tab-vborder", 0,
-                                   "group-id", 0,
                                    NULL);
 
+  /* set the group id */
+#if GTK_CHECK_VERSION (2,12,0)
+  gtk_notebook_set_group (GTK_NOTEBOOK (window->notebook), NOTEBOOK_GROUP);
+#else
+  gtk_notebook_set_group_id (GTK_NOTEBOOK (window->notebook), 1337);
+#endif
+
   /* connect signals to the notebooks */
-  g_signal_connect (G_OBJECT (window->notebook), "notify::page", G_CALLBACK (mousepad_window_page_notified), window);
-  g_signal_connect (G_OBJECT (window->notebook), "page-reordered", G_CALLBACK (mousepad_window_page_reordered), window);
-  g_signal_connect (G_OBJECT (window->notebook), "page-added", G_CALLBACK (mousepad_window_page_added), window);
-  g_signal_connect (G_OBJECT (window->notebook), "page-removed", G_CALLBACK (mousepad_window_page_removed), window);
+  g_signal_connect (G_OBJECT (window->notebook), "notify::page", G_CALLBACK (mousepad_window_notebook_notified), window);
+  g_signal_connect (G_OBJECT (window->notebook), "page-reordered", G_CALLBACK (mousepad_window_notebook_reordered), window);
+  g_signal_connect (G_OBJECT (window->notebook), "page-added", G_CALLBACK (mousepad_window_notebook_added), window);
+  g_signal_connect (G_OBJECT (window->notebook), "page-removed", G_CALLBACK (mousepad_window_notebook_removed), window);
   g_signal_connect (G_OBJECT (window->notebook), "button-press-event", G_CALLBACK (mousepad_window_notebook_button_press_event), window);
+#if GTK_CHECK_VERSION (2,12,0)
+  g_signal_connect (G_OBJECT (window->notebook), "create-window", G_CALLBACK (mousepad_window_notebook_create_window), window);
+#endif
 
   /* append and show the notebook */
   gtk_table_attach (GTK_TABLE (window->table), window->notebook, 0, 1, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
@@ -570,9 +596,8 @@ mousepad_window_init (MousepadWindow *window)
   gtk_widget_show (window->notebook);
 
   /* check if we should display the statusbar by default */
-  g_object_get (G_OBJECT (window->preferences), "last-statusbar-visible", &visible, NULL);
   action = gtk_action_group_get_action (window->window_actions, "statusbar");
-  gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
+  gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), statusbar_visible);
 
   /* allow drops in the window */
   gtk_drag_dest_set (GTK_WIDGET (window), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, drop_targets, G_N_ELEMENTS (drop_targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
@@ -646,12 +671,12 @@ mousepad_window_configure_event (GtkWidget         *widget,
       if (GTK_WIDGET_VISIBLE (widget))
         {
           /* save the geometry one second after the last configure event */
-          window->save_geometry_timer_id = g_timeout_add_full (G_PRIORITY_LOW, 1000, mousepad_window_save_geometry_timer,
+          window->save_geometry_timer_id = g_timeout_add_full (G_PRIORITY_LOW, 1000, (GSourceFunc) mousepad_window_save_geometry_timer,
                                                                window, mousepad_window_save_geometry_timer_destroy);
         }
     }
 
-  /* let Gtk+ handle the configure event */
+  /* let gtk+ handle the configure event */
   return (*GTK_WIDGET_CLASS (mousepad_window_parent_class)->configure_event) (widget, event);
 }
 
@@ -709,6 +734,7 @@ mousepad_window_menu_item_selected (GtkWidget      *menu_item,
         {
           /* read the tooltip from the action, if there is one */
           g_object_get (G_OBJECT (action), "tooltip", &tooltip, NULL);
+
           if (G_LIKELY (tooltip != NULL))
             {
               /* show the tooltip */
@@ -773,8 +799,8 @@ mousepad_window_save_geometry_timer (gpointer user_data)
 
               /* ...and remember them as default for new windows */
               g_object_set (G_OBJECT (window->preferences),
-                            "last-window-width", width,
-                            "last-window-height", height, NULL);
+                            "window-width", width,
+                            "window-height", height, NULL);
             }
         }
     }
@@ -799,13 +825,17 @@ mousepad_window_save_geometry_timer_destroy (gpointer user_data)
  **/
 gboolean
 mousepad_window_open_tab (MousepadWindow *window,
-                          const gchar    *filename)
+                          const gchar    *filename,
+                          const gchar    *encoding)
 {
-  GtkWidget   *document;
-  GError      *error = NULL;
-  gboolean     succeed = TRUE;
-  gint         npages = 0, i;
-  const gchar *opened_filename;
+  MousepadDocument *document;
+  GError           *error = NULL;
+  gboolean          succeed = FALSE;
+  gint              npages = 0, i;
+  gint              response;
+  const gchar      *new_encoding;
+  const gchar      *opened_filename;
+  GtkWidget        *dialog;
 
   /* get the number of page in the notebook */
   if (filename)
@@ -814,12 +844,12 @@ mousepad_window_open_tab (MousepadWindow *window,
   /* walk though the tabs */
   for (i = 0; i < npages; i++)
     {
-      document = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i);
+      document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i));
 
-      if (G_LIKELY (document != NULL))
+      if (G_LIKELY (document))
         {
           /* get the filename */
-          opened_filename = mousepad_document_get_filename (MOUSEPAD_DOCUMENT (document));
+          opened_filename = mousepad_file_get_filename (MOUSEPAD_DOCUMENT (document)->file);
 
           /* see if the file is already opened */
           if (opened_filename && strcmp (filename, opened_filename) == 0)
@@ -837,22 +867,83 @@ mousepad_window_open_tab (MousepadWindow *window,
   document = mousepad_document_new ();
 
   if (filename)
-    /* load the file content in the document */
-    succeed = mousepad_document_open_file (MOUSEPAD_DOCUMENT (document), filename, &error);
-
-  if (G_LIKELY (succeed))
     {
-      /* add the document to the notebook and connect some signals */
-      mousepad_window_add (window, MOUSEPAD_DOCUMENT (document));
+      /* set the filename */
+      mousepad_file_set_filename (document->file, filename);
+
+      /* set the passed encoding */
+      mousepad_file_set_encoding (document->file, encoding);
+
+      try_open_again:
+
+      /* lock the undo manager */
+      mousepad_undo_lock (document->undo);
+
+      /* read the content into the buffer */
+      succeed = mousepad_file_open (document->file, &error);
+
+      /* release the lock */
+      mousepad_undo_unlock (document->undo);
+
+      if (G_LIKELY (succeed))
+        {
+          /* add the document to the notebook and connect some signals */
+          mousepad_window_add (window, document);
+
+          /* add to the recent history */
+          mousepad_window_recent_add (window, document->file);
+        }
+      else if (error->domain == G_CONVERT_ERROR)
+        {
+          /* clear the error */
+          g_clear_error (&error);
+
+          /* run the encoding dialog */
+          dialog = mousepad_encoding_dialog_new (GTK_WINDOW (window), document->file);
+
+          /* run the dialog */
+          response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+          if (response == GTK_RESPONSE_OK)
+            {
+              /* get the selected encoding */
+              new_encoding = mousepad_encoding_dialog_get_encoding (MOUSEPAD_ENCODING_DIALOG (dialog));
+
+              /* set the document encoding */
+              mousepad_file_set_encoding (document->file, new_encoding);
+            }
+
+          /* destroy the dialog */
+          gtk_widget_destroy (dialog);
+
+          /* handle */
+          if (response == GTK_RESPONSE_OK)
+            goto try_open_again;
+          else
+            goto opening_failed;
+        }
+      else
+        {
+          opening_failed:
+
+          /* something went wrong, remove the document */
+          gtk_widget_destroy (GTK_WIDGET (document));
+
+          if (error)
+            {
+              /* show the warning */
+              mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to open file"));
+
+              g_error_free (error);
+            }
+        }
     }
   else
     {
-      /* something went wrong, remove the document */
-      gtk_widget_destroy (GTK_WIDGET (document));
+      /* no filename, simple add the window and succeed */
+      mousepad_window_add (window, document);
 
-      /* show the warning */
-      mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to open file"));
-      g_error_free (error);
+      succeed = TRUE;
     }
 
   return succeed;
@@ -891,7 +982,7 @@ mousepad_window_open_files (MousepadWindow  *window,
         }
 
       /* open a new tab with the file */
-      mousepad_window_open_tab (window, filename ? filename : filenames[n]);
+      mousepad_window_open_tab (window, filename ? filename : filenames[n], NULL);
 
       /* cleanup */
       g_free (filename);
@@ -915,110 +1006,13 @@ mousepad_window_open_files (MousepadWindow  *window,
 
 
 
-static gboolean
-mousepad_window_save (MousepadWindow   *window,
-                      MousepadDocument *document,
-                      gboolean          force_save_as)
-{
-  gchar       *new_filename = NULL;
-  const gchar *filename;
-  gboolean     succeed = FALSE;
-  GError      *error = NULL;
-  const gchar *message = NULL;
-  gint         action = MOUSEPAD_RESPONSE_OVERWRITE;
-
-  /* get the current filename */
-  filename = mousepad_document_get_filename (document);
-
-  /* check if the file really exists */
-  if (g_file_test (filename, G_FILE_TEST_EXISTS) == FALSE)
-    filename = NULL;
-
-  if (force_save_as || filename == NULL)
-    {
-      /* get the new filename */
-      new_filename = mousepad_dialogs_save_as (GTK_WINDOW (window), filename);
-
-      if (new_filename)
-        {
-          /* try the save the file */
-          succeed = mousepad_document_save_file (document, new_filename, &error);
-
-          /* the warnings message for save as */
-          message = _("Failed to save the document as another file");
-
-          if (succeed)
-            {
-              /* set the filename */
-              mousepad_document_set_filename (document, new_filename);
-
-              /* update the window title */
-              mousepad_window_set_title (window, document);
-
-              /* add the new document to the recent menu */
-              mousepad_window_recent_add (window, new_filename);
-
-              /* update the go menu */
-              mousepad_window_update_gomenu (window);
-            }
-
-          /* cleanup */
-          g_free (new_filename);
-        }
-    }
-  else
-    {
-      /* check if the file has been modified externally, if so ask the user if
-       * he or she wants to overwrite/reload or cancel the action */
-      if (G_UNLIKELY (mousepad_document_get_externally_modified (document)))
-        action = mousepad_dialogs_ask_overwrite (GTK_WINDOW (window), filename);
-
-      switch (action)
-        {
-          case MOUSEPAD_RESPONSE_OVERWRITE:
-            /* save the file */
-            succeed = mousepad_document_save_file (document, filename, &error);
-
-            /* the warning message for save */
-            message = _("Failed to save the document");
-            break;
-
-          case MOUSEPAD_RESPONSE_RELOAD:
-            /* reload the document */
-            succeed = mousepad_document_reload (document, &error);
-
-            /* the warning message for save */
-            message = _("Failed to reload the document");
-            break;
-
-          case MOUSEPAD_RESPONSE_CANCEL:
-            /* do nothing */
-            break;
-        }
-    }
-
-  /* display the error */
-  if (G_UNLIKELY (error))
-    {
-      mousepad_dialogs_show_error (GTK_WINDOW (window), error, message);
-      g_error_free (error);
-    }
-
-  return succeed;
-}
-
-
-
 void
 mousepad_window_add (MousepadWindow   *window,
                      MousepadDocument *document)
 {
   GtkWidget        *label;
-  gboolean          always_show_tabs;
-  gboolean          word_wrap, auto_indent, line_numbers;
-  gint              page, npages;
-  gchar            *font_name;
-  MousepadDocument *active;
+  gint              page;
+  MousepadDocument *prev_active;
 
   _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
@@ -1027,20 +1021,6 @@ mousepad_window_add (MousepadWindow   *window,
   /* create the tab label */
   label = mousepad_document_get_tab_label (document);
 
-  /* get the current page number */
-  page = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook));
-
-  /* update the sensitivity of the actions if this the first tab */
-  if (page == -1)
-    {
-      /* update the menu sensitivity */
-      mousepad_window_update_sensitivity (window, TRUE);
-
-      /* show the statusbar items */
-      if (window->statusbar)
-        mousepad_statusbar_visible (MOUSEPAD_STATUSBAR (window->statusbar), TRUE);
-    }
-
   /* insert the page right of the active tab */
   page = gtk_notebook_insert_page (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (document), label, page + 1);
 
@@ -1048,38 +1028,11 @@ mousepad_window_add (MousepadWindow   *window,
   gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (document), TRUE);
   gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (document), TRUE);
 
-  /* check if we should always display tabs */
-  g_object_get (G_OBJECT (window->preferences), "misc-always-show-tabs", &always_show_tabs, NULL);
-
-  /* change the visibility of the tabs accordingly */
-  npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
-  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->notebook), always_show_tabs || (npages > 1));
-
-  /* don't focus the notebook */
-  GTK_WIDGET_UNSET_FLAGS (window->notebook, GTK_CAN_FOCUS);
-
-  /* set the wrapping mode */
-  g_object_get (G_OBJECT (window->preferences), "last-word-wrap", &word_wrap, NULL);
-  mousepad_document_set_word_wrap (document, word_wrap);
-
-  /* whether line numbers are visible */
-  g_object_get (G_OBJECT (window->preferences), "last-line-numbers", &line_numbers, NULL);
-  mousepad_document_set_line_numbers (document, line_numbers);
-
-  /* whether we indent lines */
-  g_object_get (G_OBJECT (window->preferences), "last-auto-indent", &auto_indent, NULL);
-  mousepad_document_set_auto_indent (document, auto_indent);
-
-  /* set the textview font */
-  g_object_get (G_OBJECT (window->preferences), "font-name", &font_name, NULL);
-  mousepad_document_set_font (document, font_name);
-  g_free (font_name);
-
   /* show the document */
   gtk_widget_show (GTK_WIDGET (document));
 
-  /* get the active tab */
-  active = window->active;
+  /* get the active tab before we switch to the new one */
+  prev_active = window->active;
 
   /* switch to the new tab */
   gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page);
@@ -1088,11 +1041,11 @@ mousepad_window_add (MousepadWindow   *window,
   mousepad_document_focus_textview (document);
 
   /* destroy the previous tab if it was not modified, untitled and the new tab is not untitled */
-  if (active != NULL
-      && mousepad_document_get_modified (active) == FALSE
-      && mousepad_document_get_filename (active) == NULL
-      && mousepad_document_get_filename (document) != NULL)
-    gtk_widget_destroy (GTK_WIDGET (active));
+  if (prev_active != NULL
+      && gtk_text_buffer_get_modified (prev_active->buffer) == FALSE
+      && mousepad_file_get_filename (prev_active->file) == NULL
+      && mousepad_file_get_filename (document->file) != NULL)
+    gtk_widget_destroy (GTK_WIDGET (prev_active));
 }
 
 
@@ -1101,16 +1054,16 @@ static gboolean
 mousepad_window_close_document (MousepadWindow   *window,
                                 MousepadDocument *document)
 {
-  gint       response;
-  gboolean   succeed = FALSE;
+  gboolean succeed = FALSE;
+  gint     response;
 
   _mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
   _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
 
-  /* check if the document needs to be saved */
-  if (mousepad_document_get_modified (document))
+  /* check if the document has been modified */
+  if (gtk_text_buffer_get_modified (document->buffer))
     {
-      /* run the dialog */
+      /* run save changes dialog */
       response = mousepad_dialogs_save_changes (GTK_WINDOW (window));
 
       switch (response)
@@ -1119,11 +1072,13 @@ mousepad_window_close_document (MousepadWindow   *window,
             /* don't save, only destroy the document */
             succeed = TRUE;
             break;
+
           case MOUSEPAD_RESPONSE_CANCEL:
-            /* we do nothing */
+            /* do nothing */
             break;
+
           case MOUSEPAD_RESPONSE_SAVE:
-            succeed = mousepad_window_save (window, document, FALSE);
+            succeed = mousepad_window_action_save_file (NULL, window);
             break;
         }
     }
@@ -1143,30 +1098,33 @@ mousepad_window_close_document (MousepadWindow   *window,
 
 
 static void
-mousepad_window_set_title (MousepadWindow   *window,
-                           MousepadDocument *document)
+mousepad_window_set_title (MousepadWindow *window)
 {
-  gchar       *string = NULL;
-  const gchar *title;
-  gboolean     show_full_path;
+  gchar            *string;
+  const gchar      *title;
+  gboolean          show_full_path;
+  MousepadDocument *document = window->active;
 
-  if (G_LIKELY (document))
-    {
-      /* whether to show the full path in the window title */
-      g_object_get (G_OBJECT (window->preferences), "misc-show-full-path-in-title", &show_full_path, NULL);
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+  _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
 
-      /* the window filename */
-      title = mousepad_document_get_title (document, show_full_path);
+  /* whether to show the full path */
+  g_object_get (G_OBJECT (window->preferences), "misc-path-in-title", &show_full_path, NULL);
 
-      /* build the title */
-      if (G_UNLIKELY (mousepad_document_get_readonly (document)))
-        string = g_strdup_printf ("%s [%s] - %s", title, _("Read Only"), PACKAGE_NAME);
-      else
-        string = g_strdup_printf ("%s%s - %s", mousepad_document_get_modified (document) ? "*" : "", title, PACKAGE_NAME);
-    }
+  /* name we display in the title */
+  if (G_UNLIKELY (show_full_path && mousepad_document_get_filename (document)))
+    title = mousepad_document_get_filename (document);
+  else
+    title = mousepad_document_get_basename (document);
+
+  /* build the title */
+  if (G_UNLIKELY (mousepad_document_get_readonly (document)))
+    string = g_strdup_printf ("%s [%s] - %s", title, _("Read Only"), PACKAGE_NAME);
+  else
+    string = g_strdup_printf ("%s%s - %s", gtk_text_buffer_get_modified (document->buffer) ? "*" : "", title, PACKAGE_NAME);
 
   /* set the window title */
-  gtk_window_set_title (GTK_WINDOW (window), string ? string : PACKAGE_NAME);
+  gtk_window_set_title (GTK_WINDOW (window), string);
 
   /* cleanup */
   g_free (string);
@@ -1190,9 +1148,9 @@ mousepad_window_toggle_overwrite (MousepadWindow *window,
  * Notebook Signal Functions
  **/
 static void
-mousepad_window_page_notified (GtkNotebook    *notebook,
-                               GParamSpec     *pspec,
-                               MousepadWindow *window)
+mousepad_window_notebook_notified (GtkNotebook    *notebook,
+                                   GParamSpec     *pspec,
+                                   MousepadWindow *window)
 {
   gint page_num;
 
@@ -1201,35 +1159,29 @@ mousepad_window_page_notified (GtkNotebook    *notebook,
   /* get the current page */
   page_num = gtk_notebook_get_current_page (notebook);
 
-  /* get the new document */
+  /* get the new active document */
   if (G_LIKELY (page_num != -1))
     window->active = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (notebook, page_num));
   else
-    {
-      window->active = NULL;
-      _mousepad_assert_not_reached ();
-    }
+    g_assert_not_reached ();
 
-  if (G_LIKELY (window->active))
-    {
-      /* set the window title */
-      mousepad_window_set_title (window, window->active);
+  /* set the window title */
+  mousepad_window_set_title (window);
 
-      /* update the menu actions */
-      mousepad_window_update_actions (window);
+  /* update the menu actions */
+  mousepad_window_update_actions (window);
 
-      /* update the statusbar */
-      mousepad_document_send_statusbar_signals (window->active);
-    }
+  /* update the statusbar */
+  mousepad_document_send_statusbar_signals (window->active);
 }
 
 
 
 static void
-mousepad_window_page_reordered (GtkNotebook     *notebook,
-                                GtkWidget       *page,
-                                guint            page_num,
-                                MousepadWindow  *window)
+mousepad_window_notebook_reordered (GtkNotebook     *notebook,
+                                    GtkWidget       *page,
+                                    guint            page_num,
+                                    MousepadWindow  *window)
 {
   _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
   _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (page));
@@ -1241,62 +1193,86 @@ mousepad_window_page_reordered (GtkNotebook     *notebook,
 
 
 static void
-mousepad_window_page_added (GtkNotebook     *notebook,
-                            GtkWidget       *page,
-                            guint            page_num,
-                            MousepadWindow  *window)
+mousepad_window_notebook_added (GtkNotebook     *notebook,
+                                GtkWidget       *page,
+                                guint            page_num,
+                                MousepadWindow  *window)
 {
+  MousepadDocument *document = MOUSEPAD_DOCUMENT (page);
+  gboolean          always_show_tabs;
+  gint              npages;
+
   _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (page));
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
 
   /* connect signals to the document for this window */
   g_signal_connect (G_OBJECT (page), "close-tab", G_CALLBACK (mousepad_window_button_close_tab), window);
   g_signal_connect (G_OBJECT (page), "selection-changed", G_CALLBACK (mousepad_window_selection_changed), window);
-  g_signal_connect (G_OBJECT (page), "modified-changed", G_CALLBACK (mousepad_window_modified_changed), window);
   g_signal_connect (G_OBJECT (page), "cursor-changed", G_CALLBACK (mousepad_window_cursor_changed), window);
   g_signal_connect (G_OBJECT (page), "overwrite-changed", G_CALLBACK (mousepad_window_overwrite_changed), window);
   g_signal_connect (G_OBJECT (page), "drag-data-received", G_CALLBACK (mousepad_window_drag_data_received), window);
-  g_signal_connect_swapped (G_OBJECT (page), "can-undo", G_CALLBACK (mousepad_window_can_undo), window);
-  g_signal_connect_swapped (G_OBJECT (page), "can-redo", G_CALLBACK (mousepad_window_can_redo), window);
+  g_signal_connect_swapped (G_OBJECT (document->undo), "can-undo", G_CALLBACK (mousepad_window_can_undo), window);
+  g_signal_connect_swapped (G_OBJECT (document->undo), "can-redo", G_CALLBACK (mousepad_window_can_redo), window);
+  g_signal_connect_swapped (G_OBJECT (document->buffer), "modified-changed", G_CALLBACK (mousepad_window_modified_changed), window);
+
+  /* get the number of pages */
+  npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+  /* check tabs should always be visible */
+  g_object_get (G_OBJECT (window->preferences), "misc-always-show-tabs", &always_show_tabs, NULL);
+
+  /* change the visibility of the tabs accordingly */
+  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->notebook), always_show_tabs || (npages > 1));
+
+  /* don't focus the notebook */
+  GTK_WIDGET_UNSET_FLAGS (window->notebook, GTK_CAN_FOCUS);
 
   /* update the go menu */
   mousepad_window_update_gomenu (window);
 
   /* update the window actions */
   mousepad_window_update_actions (window);
-
 }
 
 
 
 static void
-mousepad_window_page_removed (GtkNotebook     *notebook,
-                              GtkWidget       *page,
-                              guint            page_num,
-                              MousepadWindow  *window)
+mousepad_window_notebook_removed (GtkNotebook     *notebook,
+                                  GtkWidget       *page,
+                                  guint            page_num,
+                                  MousepadWindow  *window)
 {
-  gboolean always_show_tabs;
-  gint     npages;
+  gboolean          always_show_tabs;
+  gint              npages;
+  MousepadDocument *document = MOUSEPAD_DOCUMENT (page);
 
   _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (page));
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
   _mousepad_return_if_fail (GTK_IS_NOTEBOOK (notebook));
 
   /* disconnect the old document signals */
   g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_button_close_tab, window);
   g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_selection_changed, window);
-  g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_modified_changed, window);
   g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_cursor_changed, window);
   g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_overwrite_changed, window);
   g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_drag_data_received, window);
-  g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_can_undo, window);
-  g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_can_redo, window);
+  g_signal_handlers_disconnect_by_func (G_OBJECT (document->undo), mousepad_window_can_undo, window);
+  g_signal_handlers_disconnect_by_func (G_OBJECT (document->undo), mousepad_window_can_redo, window);
+  g_signal_handlers_disconnect_by_func (G_OBJECT (document->buffer), mousepad_window_modified_changed, window);
+
+  /* unset the go menu item (part of the old window) */
+  g_object_set_data (G_OBJECT (page), I_("go-menu-action"), NULL);
 
   /* get the number of pages in this notebook */
   npages = gtk_notebook_get_n_pages (notebook);
 
-  /* only update if there are pages in the window */
-  if (npages > 0)
+  /* update the window */
+  if (npages == 0)
+    {
+      /* window contains no tabs, destroy it */
+      gtk_widget_destroy (GTK_WIDGET (window));
+    }
+  else
     {
       /* check tabs should always be visible */
       g_object_get (G_OBJECT (window->preferences), "misc-always-show-tabs", &always_show_tabs, NULL);
@@ -1313,35 +1289,16 @@ mousepad_window_page_removed (GtkNotebook     *notebook,
       /* update the actions */
       mousepad_window_update_actions (window);
     }
-  else
-    {
-      /* no tabs in the window, reset the active tab */
-      window->active = NULL;
-
-      /* set the new window title */
-      mousepad_window_set_title (window, NULL);
-
-      /* hide all the actions that are not relevant without tabs */
-      mousepad_window_update_sensitivity (window, FALSE);
-
-      /* hide the statusbar items */
-      if (window->statusbar)
-        mousepad_statusbar_visible (MOUSEPAD_STATUSBAR (window->statusbar), FALSE);
-
-      /* hide the search bar */
-      if (window->search_bar)
-        mousepad_window_hide_search_bar (window);
-    }
 }
 
 
 
 static void
-mousepad_window_tab_popup_position (GtkMenu  *menu,
-                                    gint     *x,
-                                    gint     *y,
-                                    gboolean *push_in,
-                                    gpointer  user_data)
+mousepad_window_notebook_menu_position (GtkMenu  *menu,
+                                        gint     *x,
+                                        gint     *y,
+                                        gboolean *push_in,
+                                        gpointer  user_data)
 {
   GtkWidget *widget = GTK_WIDGET (user_data);
 
@@ -1389,7 +1346,7 @@ mousepad_window_notebook_button_press_event (GtkNotebook    *notebook,
 
               /* show it */
               gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
-                              mousepad_window_tab_popup_position, label,
+                              (GtkMenuPositionFunc) mousepad_window_notebook_menu_position, label,
                               event->button, event->time);
 
               /* we succeed */
@@ -1403,7 +1360,7 @@ mousepad_window_notebook_button_press_event (GtkNotebook    *notebook,
   else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
     {
       /* open a new tab */
-      mousepad_window_open_tab (window, NULL);
+      mousepad_window_open_tab (window, NULL, NULL);
 
       /* we succeed */
       return TRUE;
@@ -1414,17 +1371,53 @@ mousepad_window_notebook_button_press_event (GtkNotebook    *notebook,
 
 
 
+#if GTK_CHECK_VERSION (2,12,0)
+static GtkNotebook *
+mousepad_window_notebook_create_window (GtkNotebook    *notebook,
+                                        GtkWidget      *page,
+                                        gint            x,
+                                        gint            y,
+                                        MousepadWindow *window)
+{
+  MousepadDocument *document;
+
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), NULL);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (page), NULL);
+
+  /* only create new window when there are more then 2 tabs */
+  if (gtk_notebook_get_n_pages (notebook) >= 2)
+    {
+      /* get the document */
+      document = MOUSEPAD_DOCUMENT (page);
+
+      /* take a reference */
+      g_object_ref (G_OBJECT (document));
+
+      /* remove the document from the active window */
+      gtk_container_remove (GTK_CONTAINER (window->notebook), page);
+
+      /* emit the new window with document signal */
+      g_signal_emit (G_OBJECT (window), window_signals[NEW_WINDOW_WITH_DOCUMENT], 0, document, x, y);
+
+      /* release our reference */
+      g_object_unref (G_OBJECT (document));
+    }
+
+  return NULL;
+}
+#endif
+
+
+
 /**
  * Document Signals Functions
  **/
 static void
-mousepad_window_modified_changed (MousepadDocument *document,
-                                  MousepadWindow   *window)
+mousepad_window_modified_changed (MousepadWindow   *window)
 {
   _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
-  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
 
-  mousepad_window_set_title (window, document);
+  mousepad_window_set_title (window);
 }
 
 
@@ -1518,11 +1511,13 @@ mousepad_window_update_actions (MousepadWindow *window)
   gboolean          has_selection;
   gboolean          active;
 
+  _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
   /* active document */
   document = window->active;
 
   /* update the actions for the active document */
-  if (G_LIKELY (document != NULL))
+  if (G_LIKELY (document))
     {
       /* avoid menu actions */
       lock_menu_updates++;
@@ -1541,32 +1536,39 @@ mousepad_window_update_actions (MousepadWindow *window)
       action = gtk_action_group_get_action (window->window_actions, "forward");
       gtk_action_set_sensitive (action, (cycle_tabs && n_pages > 1 ) || (page_num < n_pages - 1));
 
-      /* set the reload and save sensitivity */
+      /* set the reload, detach and save sensitivity */
       action = gtk_action_group_get_action (window->window_actions, "save-file");
       gtk_action_set_sensitive (action, !mousepad_document_get_readonly (document));
 
+      action = gtk_action_group_get_action (window->window_actions, "detach-tab");
+      gtk_action_set_sensitive (action, (n_pages > 1));
+
       action = gtk_action_group_get_action (window->window_actions, "reload");
-      gtk_action_set_sensitive (action, mousepad_document_get_filename (document) != NULL);
+      gtk_action_set_sensitive (action, mousepad_file_get_filename (document->file) != NULL);
 
       /* toggle the document settings */
       active = mousepad_document_get_word_wrap (document);
       action = gtk_action_group_get_action (window->window_actions, "word-wrap");
       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
 
-      active = mousepad_document_get_line_numbers (document);
+      active = mousepad_view_get_line_numbers (document->textview);
       action = gtk_action_group_get_action (window->window_actions, "line-numbers");
       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
 
-      active = mousepad_document_get_auto_indent (document);
+      active = mousepad_view_get_auto_indent (document->textview);
       action = gtk_action_group_get_action (window->window_actions, "auto-indent");
       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
 
+      active = mousepad_view_get_insert_spaces (document->textview);
+      action = gtk_action_group_get_action (window->window_actions, "insert-spaces");
+      gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
+
       /* set the sensitivity of the undo and redo actions */
-      mousepad_window_can_undo (window, mousepad_document_get_can_undo (document));
-      mousepad_window_can_redo (window, mousepad_document_get_can_redo (document));
+      mousepad_window_can_undo (window, mousepad_undo_can_undo (document->undo));
+      mousepad_window_can_redo (window, mousepad_undo_can_redo (document->undo));
 
       /* set the sensitivity of the selection actions */
-      has_selection = mousepad_document_get_has_selection (document);
+      has_selection = mousepad_view_get_has_selection (document->textview);
       mousepad_window_selection_changed (document, has_selection, window);
 
       /* active this tab in the go menu */
@@ -1581,47 +1583,20 @@ mousepad_window_update_actions (MousepadWindow *window)
 
 
 
-static void
-mousepad_window_update_sensitivity (MousepadWindow *window,
-                                    gboolean        sensitive)
-{
-  gint         i;
-  GtkAction   *action;
-  const gchar *actions[] = {"edit-menu", "search-menu", "document-menu", "go-menu",
-                            "save-file", "save-file-as", "reload", "print-document",
-                            "detach-tab", "close-tab"};
-
-  /* walk through the actions */
-  for (i = 0; i < G_N_ELEMENTS (actions); i++)
-    {
-      /* get the action */
-      action = gtk_action_group_get_action (window->window_actions, actions[i]);
-
-      /* leave when the first action already has the right state */
-      if (G_LIKELY (i == 0 && (gtk_action_get_sensitive (action) == sensitive)))
-        break;
-
-      /* set the sensitivity */
-      gtk_action_set_sensitive (action, sensitive);
-    }
-}
-
-
-
 static gboolean
 mousepad_window_update_gomenu_idle (gpointer user_data)
 {
-  GtkWidget      *document;
-  MousepadWindow *window;
-  gint            npages;
-  gint            n;
-  gchar           name[15];
-  const gchar    *title;
-  const gchar    *tooltip;
-  gchar           accelerator[7];
-  GtkRadioAction *action;
-  GSList         *group = NULL;
-  GList          *actions, *li;
+  MousepadDocument *document;
+  MousepadWindow   *window;
+  gint              npages;
+  gint              n;
+  gchar             name[15];
+  const gchar      *title;
+  const gchar      *tooltip;
+  gchar             accelerator[7];
+  GtkRadioAction   *action;
+  GSList           *group = NULL;
+  GList            *actions, *li;
 
   _mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (user_data), FALSE);
 
@@ -1651,14 +1626,14 @@ mousepad_window_update_gomenu_idle (gpointer user_data)
 
   for (n = 0; n < npages; ++n)
     {
-      document = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), n);
+      document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), n));
 
       /* create a new action name */
       g_snprintf (name, sizeof (name), "document-%d", n);
 
       /* get the name and file name */
-      title = mousepad_document_get_title (MOUSEPAD_DOCUMENT (document), FALSE);
-      tooltip = mousepad_document_get_title (MOUSEPAD_DOCUMENT (document), TRUE);
+      title = mousepad_document_get_basename (document);
+      tooltip = mousepad_document_get_filename (document);
 
       /* create the radio action */
       action = gtk_radio_action_new (name, title, tooltip, NULL, n);
@@ -1726,7 +1701,7 @@ mousepad_window_update_gomenu (MousepadWindow *window)
     return;
 
   /* schedule a go menu update */
-  window->update_go_menu_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_window_update_gomenu_idle,
+  window->update_go_menu_id = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) mousepad_window_update_gomenu_idle,
                                                window, mousepad_window_update_gomenu_idle_destroy);
 }
 
@@ -1737,17 +1712,22 @@ mousepad_window_update_gomenu (MousepadWindow *window)
  **/
 static void
 mousepad_window_recent_add (MousepadWindow *window,
-                            const gchar    *filename)
+                            MousepadFile   *file)
 {
   GtkRecentData  info;
   gchar         *uri;
-  static gchar  *groups[] = { PACKAGE_NAME, NULL, };
+  static gchar  *groups[] = { PACKAGE_NAME, NULL };
+  gchar         *description;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+  _mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
 
-  _mousepad_return_if_fail (filename != NULL);
+  /* build description */
+  description = g_strdup_printf ("%s: %s", _("Encoding"), mousepad_file_get_encoding (file));
 
   /* create the recent data */
   info.display_name = NULL;
-  info.description  = NULL;
+  info.description  = description;
   info.mime_type    = "text/plain";
   info.app_name     = PACKAGE_NAME;
   info.app_exec     = PACKAGE " %u";
@@ -1755,7 +1735,7 @@ mousepad_window_recent_add (MousepadWindow *window,
   info.is_private   = FALSE;
 
   /* create an uri from the filename */
-  uri = g_filename_to_uri (filename, NULL, NULL);
+  uri = mousepad_file_get_uri (file);
 
   if (G_LIKELY (uri != NULL))
     {
@@ -1765,6 +1745,9 @@ mousepad_window_recent_add (MousepadWindow *window,
       /* cleanup */
       g_free (uri);
     }
+
+  /* cleanup */
+  g_free (description);
 }
 
 
@@ -1858,7 +1841,7 @@ mousepad_window_recent_menu_idle (gpointer user_data)
   filtered = g_list_sort (filtered, (GCompareFunc) mousepad_window_recent_sort);
 
   /* get the recent menu limit number */
-  g_object_get (G_OBJECT (window->preferences), "misc-recent-menu-limit", &n, NULL);
+  g_object_get (G_OBJECT (window->preferences), "misc-recent-menu-items", &n, NULL);
 
   /* append the items to the menu */
   for (li = filtered; n > 0 && li != NULL; li = li->next, --n)
@@ -1942,7 +1925,7 @@ mousepad_window_recent_menu (MousepadWindow *window)
     return;
 
   /* schedule a recent menu update */
-  window->update_recent_menu_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_window_recent_menu_idle,
+  window->update_recent_menu_id = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) mousepad_window_recent_menu_idle,
                                                    window, mousepad_window_recent_menu_idle_destroy);
 }
 
@@ -1984,7 +1967,7 @@ mousepad_window_recent_clear (MousepadWindow *window)
   if (G_UNLIKELY (error != NULL))
     {
       mousepad_dialogs_show_error (GTK_WINDOW (window), error,
-                                   _("Failed to remove an item from the document history"));
+                                   _("Failed to remove an item from the documents history"));
       g_error_free (error);
     }
 }
@@ -2075,13 +2058,70 @@ mousepad_window_drag_data_received (GtkWidget        *widget,
 
 
 
+static gint
+mousepad_window_search (MousepadWindow      *window,
+                        MousepadSearchFlags  flags,
+                        const gchar         *string,
+                        const gchar         *replacement)
+{
+  gint       nmatches = 0;
+  gint       npages, i;
+  GtkWidget *document;
+
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), -1);
+
+  if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT)
+    {
+      /* highlight all the matches */
+      nmatches = mousepad_util_highlight (window->active->buffer, window->active->tag, string, flags);
+    }
+  else if (flags & MOUSEPAD_SEARCH_FLAGS_ALL_DOCUMENTS)
+    {
+      /* get the number of documents in this window */
+      npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+      /* walk the pages */
+      for (i = 0; i < npages; i++)
+        {
+          /* get the document */
+          document = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i);
+
+          /* replace the matches in the document */
+          nmatches += mousepad_util_search (MOUSEPAD_DOCUMENT (document)->buffer, string, replacement, flags);
+        }
+    }
+  else if (window->active != NULL)
+    {
+      /* search or replace in the active document */
+      nmatches = mousepad_util_search (window->active->buffer, string, replacement, flags);
+
+      /* make sure the selection is visible */
+      if (flags & (MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT | MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE) && nmatches > 0)
+        mousepad_view_put_cursor_on_screen (window->active->textview);
+    }
+  else
+    {
+      /* should never be reaches */
+      _mousepad_assert_not_reached ();
+    }
+
+  return nmatches;
+}
+
+
+
 /**
  * Search Bar
  **/
 static void
 mousepad_window_hide_search_bar (MousepadWindow *window)
 {
- _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+  _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+  _mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (window->search_bar));
+
+  /* remove the highlight */
+  //mousepad_search_bar_reset_highlight (MOUSEPAD_SEARCH_BAR (window->search_bar));
+  /* TODO */
 
   /* hide the search bar */
   gtk_widget_hide (window->search_bar);
@@ -2093,52 +2133,20 @@ mousepad_window_hide_search_bar (MousepadWindow *window)
 }
 
 
-
-static gboolean
-mousepad_window_find_string (MousepadWindow      *window,
-                             const gchar         *string,
-                             MousepadSearchFlags  flags)
-{
-  gboolean found = FALSE;
-
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
-  _mousepad_return_val_if_fail (g_utf8_validate (string, -1, NULL), FALSE);
-
-  if (G_LIKELY (window->active))
-    found = mousepad_document_find (window->active, string, flags);
-
-  return found;
-}
-
-static gboolean
-mousepad_window_highlight_all (MousepadWindow      *window,
-                               const gchar         *string,
-                               MousepadSearchFlags  flags)
-{
-  _mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
-  _mousepad_return_val_if_fail (g_utf8_validate (string, -1, NULL), FALSE);
-
-  /* hightlight all the occurences in the active document */
-  if (G_LIKELY (window->active))
-    mousepad_document_highlight_all (window->active, string, flags);
-
-  return FALSE;
-}
-
-
-
 /**
  * Menu Actions
  *
  * All those function should be sorted by the menu structure so it's
- * easy to find a function. They should also use the other window
- * functions as much as possible to avoid code duplication.
+ * easy to find a function. Remember that some action functions can
+ * still be triggered with keybindings, even when there are no tabs
+ * opened. There for it should always check if the active document is
+ * not NULL.
  **/
 static void
 mousepad_window_action_open_new_tab (GtkAction      *action,
                                      MousepadWindow *window)
 {
-  mousepad_window_open_tab (window, NULL);
+  mousepad_window_open_tab (window, NULL, NULL);
 }
 
 
@@ -2157,10 +2165,11 @@ static void
 mousepad_window_action_open_file (GtkAction      *action,
                                   MousepadWindow *window)
 {
-  GtkWidget   *chooser;
-  gchar       *filename;
-  const gchar *active_filename;
-  GSList      *filenames, *li;
+  GtkWidget        *chooser;
+  gchar            *filename;
+  const gchar      *active_filename;
+  GSList           *filenames, *li;
+  MousepadDocument *document = window->active;
 
   /* create new chooser dialog */
   chooser = gtk_file_chooser_dialog_new (_("Open File"),
@@ -2174,10 +2183,10 @@ mousepad_window_action_open_file (GtkAction      *action,
   gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
 
   /* open the folder of the currently opened file */
-  if (window->active)
+  if (document)
     {
       /* get the filename of the active document */
-      active_filename = mousepad_document_get_filename (window->active);
+      active_filename = mousepad_file_get_filename (document->file);
 
       /* set the current filename, if there is one */
       if (active_filename && g_file_test (active_filename, G_FILE_TEST_EXISTS))
@@ -2187,6 +2196,9 @@ mousepad_window_action_open_file (GtkAction      *action,
   /* run the dialog */
   if (G_LIKELY (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT))
     {
+      /* hide the dialog */
+      gtk_widget_hide (chooser);
+
       /* open the new file */
       filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (chooser));
 
@@ -2199,10 +2211,7 @@ mousepad_window_action_open_file (GtkAction      *action,
           filename = li->data;
 
           /* open the file in a new tab */
-          mousepad_window_open_tab (window, filename);
-
-          /* add to the recent files */
-          mousepad_window_recent_add (window, filename);
+          mousepad_window_open_tab (window, filename, NULL);
 
           /* cleanup */
           g_free (filename);
@@ -2229,8 +2238,10 @@ static void
 mousepad_window_action_open_recent (GtkAction      *action,
                                     MousepadWindow *window)
 {
-  const gchar   *uri;
+  const gchar   *uri, *description;
+  const gchar   *encoding = NULL;
   GError        *error = NULL;
+  gint           offset;
   gchar         *filename;
   gboolean       succeed = FALSE;
   GtkRecentInfo *info;
@@ -2243,14 +2254,24 @@ mousepad_window_action_open_recent (GtkAction      *action,
       /* get the file uri */
       uri = gtk_recent_info_get_uri (info);
 
-      /* get the filename from the uri */
+      /* build a filename from the uri */
       filename = g_filename_from_uri (uri, NULL, NULL);
 
       if (G_LIKELY (filename != NULL))
         {
           /* open the file in a new tab if it exists */
           if (g_file_test (filename, G_FILE_TEST_EXISTS))
-            succeed = mousepad_window_open_tab (window, filename);
+            {
+              /* set text offset */
+              offset = strlen (_("Encoding")) + 2;
+
+              /* try to read the encoding we might has saved in the description ;) */
+              description = gtk_recent_info_get_description (info);
+              if (G_LIKELY (description && strlen (description) > offset))
+                encoding = description + offset;
+
+              succeed = mousepad_window_open_tab (window, filename, encoding);
+            }
           else
             {
               /* create a warning */
@@ -2262,16 +2283,14 @@ mousepad_window_action_open_recent (GtkAction      *action,
               g_error_free (error);
             }
 
+          /* cleanup */
+          g_free (filename);
+
           /* update the document history */
           if (G_LIKELY (succeed))
-            /* update the recent manager count and time */
             gtk_recent_manager_add_item (window->recent_manager, uri);
           else
-            /* remove the item from the history */
             gtk_recent_manager_remove_item (window->recent_manager, uri, NULL);
-
-          /* cleanup */
-          g_free (filename);
         }
     }
 }
@@ -2301,39 +2320,93 @@ mousepad_window_action_clear_recent (GtkAction      *action,
 
 
 
-static void
+static gboolean
 mousepad_window_action_save_file (GtkAction      *action,
                                   MousepadWindow *window)
 {
-  MousepadDocument *document;
+  MousepadDocument *document = window->active;
+  GError           *error = NULL;
+  gboolean          succeed = FALSE;
 
-  document = window->active;
-  if (G_LIKELY (document != NULL))
+  _mousepad_return_val_if_fail (document != NULL, FALSE);
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+
+  if (G_LIKELY (document))
     {
-      /* save the file */
-      mousepad_window_save (window, document, FALSE);
+      /* save-as when there is no filename yet */
+      if (mousepad_file_get_filename (document->file) == NULL)
+        {
+          mousepad_window_action_save_file_as (NULL, window);
+        }
+      else
+        {
+          /* save the document */
+          succeed = mousepad_file_save (document->file, &error);
+
+          if (G_UNLIKELY (succeed == FALSE))
+            {
+              /* show the warning */
+              mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to save the document"));
+              g_error_free (error);
+            }
+        }
     }
+
+  return succeed;
 }
 
 
 
-static void
+static gboolean
 mousepad_window_action_save_file_as (GtkAction      *action,
                                      MousepadWindow *window)
 {
-  MousepadDocument *document;
+  MousepadDocument *document = window->active;
+  gchar            *filename;
+  const gchar      *old_filename;
+  GtkWidget        *dialog;
+  gboolean          succeed = TRUE;
 
-  document = window->active;
-  if (G_LIKELY (document != NULL))
+  if (G_LIKELY (document))
     {
-      /* save the file */
-      if (mousepad_window_save (window, document, TRUE))
+      /* create the dialog */
+      dialog = gtk_file_chooser_dialog_new (_("Save As"),
+          GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_SAVE,
+          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+          GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
+      gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
+      gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
+      gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+      /* set the current filename */
+      old_filename = mousepad_file_get_filename (document->file);
+      if (old_filename)
+        gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), old_filename);
+
+      /* run the dialog */
+      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
         {
-          /* make sure save-file is sensitive */
-          action = gtk_action_group_get_action (window->window_actions, "save-file");
-          gtk_action_set_sensitive (action, TRUE);
+          /* get the new filename */
+          filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+          if (G_LIKELY (filename))
+            {
+              /* set the new filename */
+              mousepad_file_set_filename (document->file, filename);
+
+              /* cleanup */
+              g_free (filename);
+
+              /* save the file */
+              succeed = mousepad_window_action_save_file (NULL, window);
+            }
         }
+
+      /* destroy the dialog */
+      gtk_widget_destroy (dialog);
     }
+
+  return succeed;
 }
 
 
@@ -2342,41 +2415,32 @@ static void
 mousepad_window_action_reload (GtkAction      *action,
                                MousepadWindow *window)
 {
-  MousepadDocument *document;
+  MousepadDocument *document = window->active;
   GError           *error = NULL;
-  const gchar      *message = NULL;
-  gint              response = MOUSEPAD_RESPONSE_RELOAD;
+  gboolean          result;
 
-  document = window->active;
-  if (G_LIKELY (document != NULL))
+  if (G_LIKELY (document))
     {
-      /* ask what to do when the document still has modifications */
-      if (mousepad_document_get_modified (document))
-        response = mousepad_dialogs_ask_reload (GTK_WINDOW (window));
+      /* TODO ask user if he/she really want this */
 
-      switch (response)
-        {
-          case MOUSEPAD_RESPONSE_CANCEL:
-            /* do nothing */
-            break;
+      /* lock the undo manager */
+      mousepad_undo_lock (document->undo);
 
-          case MOUSEPAD_RESPONSE_SAVE_AS:
-            /* try to save the document, break when this went wrong, else
-             * fall-though and try to reload the document */
-            if (!mousepad_window_save (window, document, TRUE))
-              break;
+      /* clear the undo history */
+      mousepad_undo_clear (document->undo);
 
-          case MOUSEPAD_RESPONSE_RELOAD:
-            if (!mousepad_document_reload (document, &error))
-              message = _("Failed to reload the document");
-            break;
-        }
-    }
+      /* reload the file */
+      result = mousepad_file_reload (document->file, &error);
 
-  if (G_UNLIKELY (error != NULL))
-    {
-      mousepad_dialogs_show_error (GTK_WINDOW (window), error, message);
-      g_error_free (error);
+      /* release the lock */
+      mousepad_undo_unlock (document->undo);
+
+      if (G_UNLIKELY (result == FALSE))
+        {
+          /* show the warning */
+          mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to reload the document"));
+          g_error_free (error);
+        }
     }
 }
 
@@ -2386,7 +2450,19 @@ static void
 mousepad_window_action_print (GtkAction      *action,
                               MousepadWindow *window)
 {
+  MousepadPrint    *print;
+  MousepadDocument *document = window->active;
+  GError           *error = NULL;
+  gboolean          succeed;
 
+  /* create new print operation */
+  print = mousepad_print_new ();
+
+  /* print the current document interactive */
+  succeed = mousepad_print_document_interactive (print, document, GTK_WINDOW (window), &error);
+
+  /* release the object */
+  g_object_unref (G_OBJECT (print));
 }
 
 
@@ -2395,10 +2471,13 @@ static void
 mousepad_window_action_detach (GtkAction      *action,
                                MousepadWindow *window)
 {
-  MousepadDocument *document;
-
-  /* get the active tab */
-  document = window->active;
+#if GTK_CHECK_VERSION (2,12,0)
+  /* invoke function without cooridinates */
+  mousepad_window_notebook_create_window (GTK_NOTEBOOK (window->notebook),
+                                          GTK_WIDGET (window->active),
+                                          -1, -1, window);
+#else
+  MousepadDocument *document = window->active;
 
   /* only detach when there are more then 2 tabs */
   if (document && gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)) >= 2)
@@ -2410,11 +2489,12 @@ mousepad_window_action_detach (GtkAction      *action,
       gtk_container_remove (GTK_CONTAINER (window->notebook), GTK_WIDGET (document));
 
       /* emit the new window with document signal */
-      g_signal_emit (G_OBJECT (window), window_signals[NEW_WINDOW_WITH_DOCUMENT], 0, document);
+      g_signal_emit (G_OBJECT (window), window_signals[NEW_WINDOW_WITH_DOCUMENT], 0, document, -1, -1);
 
       /* release our reference */
       g_object_unref (G_OBJECT (document));
     }
+#endif
 }
 
 
@@ -2423,10 +2503,9 @@ static void
 mousepad_window_action_close_tab (GtkAction      *action,
                                   MousepadWindow *window)
 {
-  MousepadDocument *document;
+  MousepadDocument *document = window->active;
 
-  document = window->active;
-  if (G_LIKELY (document != NULL))
+  if (G_LIKELY (document))
     mousepad_window_close_document (window, document);
 }
 
@@ -2450,9 +2529,9 @@ mousepad_window_action_close (GtkAction      *action,
     {
       document = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i);
 
-      if (G_LIKELY (document != NULL))
+      if (G_LIKELY (document))
         {
-          /* switch to the tab we're going to close */
+          /* focus the tab we're going to close */
           gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), i);
 
           /* ask user what to do, break when he/she hits the cancel button */
@@ -2470,8 +2549,7 @@ mousepad_window_action_close (GtkAction      *action,
         }
     }
 
-  /* destroy the window */
-  gtk_widget_destroy (GTK_WIDGET (window));
+  /* window will close it self when it contains to tabs */
 }
 
 
@@ -2480,8 +2558,15 @@ static void
 mousepad_window_action_undo (GtkAction      *action,
                              MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_undo (window->active);
+  MousepadDocument *document = window->active;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+  /* undo */
+  mousepad_undo_do_undo (document->undo);
+
+  /* scroll to visible area */
+  mousepad_view_put_cursor_on_screen (document->textview);
 }
 
 
@@ -2490,8 +2575,15 @@ static void
 mousepad_window_action_redo (GtkAction      *action,
                              MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_redo (window->active);
+  MousepadDocument *document = window->active;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+  /* redo */
+  mousepad_undo_do_redo (document->undo);
+
+  /* scroll to visible area */
+  mousepad_view_put_cursor_on_screen (document->textview);
 }
 
 
@@ -2500,8 +2592,17 @@ static void
 mousepad_window_action_cut (GtkAction      *action,
                             MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_cut_selection (window->active);
+  GtkEditable *entry;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+  /* get searchbar entry */
+  entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+  if (entry)
+    gtk_editable_cut_clipboard (entry);
+  else
+    mousepad_view_clipboard_cut (window->active->textview);
 }
 
 
@@ -2510,8 +2611,17 @@ static void
 mousepad_window_action_copy (GtkAction      *action,
                              MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_copy_selection (window->active);
+  GtkEditable *entry;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+  /* get searchbar entry */
+  entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+  if (entry)
+    gtk_editable_copy_clipboard (entry);
+  else
+    mousepad_view_clipboard_copy (window->active->textview);
 }
 
 
@@ -2520,8 +2630,17 @@ static void
 mousepad_window_action_paste (GtkAction      *action,
                               MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_paste_clipboard (window->active);
+  GtkEditable *entry;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+  /* get searchbar entry */
+  entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+  if (entry)
+    gtk_editable_paste_clipboard (entry);
+  else
+    mousepad_view_clipboard_paste (window->active->textview, FALSE);
 }
 
 
@@ -2530,8 +2649,11 @@ static void
 mousepad_window_action_paste_column (GtkAction      *action,
                                      MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_paste_column_clipboard (window->active);
+  MousepadDocument *document = window->active;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+  mousepad_view_clipboard_paste (document->textview, TRUE);
 }
 
 
@@ -2540,8 +2662,17 @@ static void
 mousepad_window_action_delete (GtkAction      *action,
                                MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_delete_selection (window->active);
+  GtkEditable *entry;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+  /* get searchbar entry */
+  entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+  if (entry)
+    gtk_editable_delete_selection (entry);
+  else
+    mousepad_view_delete_selection (window->active->textview);
 }
 
 
@@ -2550,8 +2681,11 @@ static void
 mousepad_window_action_select_all (GtkAction      *action,
                                    MousepadWindow *window)
 {
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_select_all (window->active);
+  MousepadDocument *document = window->active;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+  mousepad_view_select_all (document->textview);
 }
 
 
@@ -2560,27 +2694,23 @@ static void
 mousepad_window_action_find (GtkAction      *action,
                              MousepadWindow *window)
 {
-  if (G_LIKELY (window->active))
+  if (window->search_bar == NULL)
     {
-      if (G_UNLIKELY (window->search_bar == NULL))
-        {
-          /* create a new toolbar */
-          window->search_bar = mousepad_search_bar_new ();
-          gtk_table_attach (GTK_TABLE (window->table), window->search_bar, 0, 1, 4, 5, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
-
-          /* connect signals to the search bar */
-          g_signal_connect_swapped (G_OBJECT (window->search_bar), "hide-bar", G_CALLBACK (mousepad_window_hide_search_bar), window);
-          g_signal_connect_swapped (G_OBJECT (window->search_bar), "find-string", G_CALLBACK (mousepad_window_find_string), window);
-          g_signal_connect_swapped (G_OBJECT (window->search_bar), "highlight-all", G_CALLBACK (mousepad_window_highlight_all), window);
-        }
+      /* create a new toolbar */
+      window->search_bar = mousepad_search_bar_new ();
+      gtk_table_attach (GTK_TABLE (window->table), window->search_bar, 0, 1, 4, 5, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
-      /* show the search bar and give some space to the table */
-      gtk_widget_show (window->search_bar);
-      gtk_table_set_row_spacing (GTK_TABLE (window->table), 3, WINDOW_SPACING);
-
-      /* focus the search entry */
-      mousepad_search_bar_focus (MOUSEPAD_SEARCH_BAR (window->search_bar));
+      /* connect signals */
+      g_signal_connect_swapped (G_OBJECT (window->search_bar), "hide-bar", G_CALLBACK (mousepad_window_hide_search_bar), window);
+      g_signal_connect_swapped (G_OBJECT (window->search_bar), "search", G_CALLBACK (mousepad_window_search), window);
     }
+
+  /* show the search bar and give some space to the table */
+  gtk_widget_show (window->search_bar);
+  gtk_table_set_row_spacing (GTK_TABLE (window->table), 3, WINDOW_SPACING);
+
+  /* focus the search entry */
+  mousepad_search_bar_focus (MOUSEPAD_SEARCH_BAR (window->search_bar));
 }
 
 
@@ -2589,8 +2719,10 @@ static void
 mousepad_window_action_find_next (GtkAction      *action,
                                   MousepadWindow *window)
 {
+  MousepadDocument *document = window->active;
+
   /* only find the next occurence when the search bar is initialized */
-  if (G_LIKELY (window->active && window->search_bar != NULL))
+  if (G_LIKELY (document && window->search_bar != NULL))
     mousepad_search_bar_find_next (MOUSEPAD_SEARCH_BAR (window->search_bar));
 }
 
@@ -2600,18 +2732,45 @@ static void
 mousepad_window_action_find_previous (GtkAction      *action,
                                       MousepadWindow *window)
 {
+  MousepadDocument *document = window->active;
+
+  _mousepad_return_if_fail (document != NULL);
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
   /* only find the previous occurence when the search bar is initialized */
-  if (G_LIKELY (window->active && window->search_bar != NULL))
+  if (G_LIKELY (document && window->search_bar != NULL))
     mousepad_search_bar_find_previous (MOUSEPAD_SEARCH_BAR (window->search_bar));
 }
 
 
 
 static void
+mousepad_window_action_replace_destroy (MousepadWindow *window)
+{
+  /* reset the dialog variable */
+  window->replace_dialog = NULL;
+}
+
+
+
+static void
 mousepad_window_action_replace (GtkAction      *action,
                                 MousepadWindow *window)
 {
+  if (!window->replace_dialog)
+    {
+      /* create a new dialog */
+      window->replace_dialog = mousepad_replace_dialog_new ();
 
+      /* popup the dialog */
+      gtk_window_set_destroy_with_parent (GTK_WINDOW (window->replace_dialog), TRUE);
+      gtk_window_set_transient_for (GTK_WINDOW (window->replace_dialog), GTK_WINDOW (window));
+      gtk_widget_show (window->replace_dialog);
+
+      /* connect signals */
+      g_signal_connect_swapped (G_OBJECT (window->replace_dialog), "destroy", G_CALLBACK (mousepad_window_action_replace_destroy), window);
+      g_signal_connect_swapped (G_OBJECT (window->replace_dialog), "search", G_CALLBACK (mousepad_window_search), window);
+    }
 }
 
 
@@ -2620,12 +2779,10 @@ static void
 mousepad_window_action_jump_to (GtkAction      *action,
                                 MousepadWindow *window)
 {
-  MousepadDocument *document;
+  MousepadDocument *document = window->active;
   gint              current_line, last_line, line;
 
-  /* get the active document */
-  document = window->active;
-  if (G_LIKELY (document != NULL))
+  if (G_LIKELY (document))
     {
       /* get the current and last line number */
       mousepad_document_line_numbers (document, &current_line, &last_line);
@@ -2644,16 +2801,16 @@ static void
 mousepad_window_action_select_font (GtkAction      *action,
                                     MousepadWindow *window)
 {
-  GtkWidget *dialog;
-  GtkWidget *document;
-  gchar     *font_name;
-  guint      npages, i;
+  GtkWidget        *dialog;
+  MousepadDocument *document;
+  gchar            *font_name;
+  guint             npages, i;
 
   dialog = gtk_font_selection_dialog_new (_("Choose Mousepad Font"));
   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
 
   /* get the current font */
-  g_object_get (G_OBJECT (window->preferences), "font-name", &font_name, NULL);
+  g_object_get (G_OBJECT (window->preferences), "view-font-name", &font_name, NULL);
 
   if (G_LIKELY (font_name))
     {
@@ -2666,15 +2823,23 @@ mousepad_window_action_select_font (GtkAction      *action,
     {
       /* send the new font to the preferences */
       font_name = gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG (dialog));
-      g_object_set (G_OBJECT (window->preferences), "font-name", font_name, NULL);
+      g_object_set (G_OBJECT (window->preferences), "view-font-name", font_name, NULL);
 
-      /* send the new font to all tabs in this window */
+      /* set the font in all documents in this window */
       npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
       for (i = 0; i < npages; i++)
         {
-          document = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i);
-          if (G_LIKELY (document != NULL))
-            mousepad_document_set_font (MOUSEPAD_DOCUMENT (document), font_name);
+          /* get the document */
+          document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i));
+
+          /* debug check */
+          _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+          /* set the font */
+          mousepad_document_set_font (document, font_name);
+
+          /* update the tab array */
+          mousepad_view_set_tab_width (document->textview, mousepad_view_get_tab_width (document->textview));
         }
 
       /* cleanup */
@@ -2691,19 +2856,19 @@ static void
 mousepad_window_action_statusbar (GtkToggleAction *action,
                                   MousepadWindow  *window)
 {
-  gboolean active;
+  gboolean show_statusbar;
 
-  /* determine the new state of the action */
-  active = gtk_toggle_action_get_active (action);
+  /* whether we show the statusbar */
+  show_statusbar = gtk_toggle_action_get_active (action);
 
   /* check if we should drop the statusbar */
-  if (!active && window->statusbar != NULL)
+  if (!show_statusbar && window->statusbar != NULL)
     {
       /* just get rid of the statusbar */
       gtk_widget_destroy (window->statusbar);
       window->statusbar = NULL;
     }
-  else if (active && window->statusbar == NULL)
+  else if (show_statusbar && window->statusbar == NULL)
     {
       /* setup a new statusbar */
       window->statusbar = mousepad_statusbar_new ();
@@ -2714,22 +2879,16 @@ mousepad_window_action_statusbar (GtkToggleAction *action,
       g_signal_connect_swapped (G_OBJECT (window->statusbar), "enable-overwrite",
                                 G_CALLBACK (mousepad_window_toggle_overwrite), window);
 
-      /* set the statsbar text */
-      if (window->active != NULL)
-        {
-          /* show the statusbar items */
-          mousepad_statusbar_visible (MOUSEPAD_STATUSBAR (window->statusbar), TRUE);
-
-          /* update the statusbar items */
-          mousepad_document_send_statusbar_signals (window->active);
-        }
+      /* update the statusbar items */
+      if (window->active)
+        mousepad_document_send_statusbar_signals (window->active);
     }
 
   /* set the spacing above the statusbar */
-  gtk_table_set_row_spacing (GTK_TABLE (window->table), 4, active ? WINDOW_SPACING : 0);
+  gtk_table_set_row_spacing (GTK_TABLE (window->table), 4, show_statusbar ? WINDOW_SPACING : 0);
 
   /* remember the setting */
-  g_object_set (G_OBJECT (window->preferences), "last-statusbar-visible", active, NULL);
+  g_object_set (G_OBJECT (window->preferences), "window-statusbar-visible", show_statusbar, NULL);
 }
 
 
@@ -2740,6 +2899,8 @@ mousepad_window_action_word_wrap (GtkToggleAction *action,
 {
   gboolean word_wrap;
 
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
   /* leave when menu updates are locked */
   if (lock_menu_updates)
     return;
@@ -2748,11 +2909,10 @@ mousepad_window_action_word_wrap (GtkToggleAction *action,
   word_wrap = gtk_toggle_action_get_active (action);
 
   /* store this as the last used wrap mode */
-  g_object_set (G_OBJECT (window->preferences), "last-word-wrap", word_wrap, NULL);
+  g_object_set (G_OBJECT (window->preferences), "view-word-wrap", word_wrap, NULL);
 
   /* set the wrapping mode of the current document */
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_set_word_wrap (window->active, word_wrap);
+  mousepad_document_set_word_wrap (window->active, word_wrap);
 }
 
 
@@ -2763,6 +2923,8 @@ mousepad_window_action_line_numbers (GtkToggleAction *action,
 {
   gboolean line_numbers;
 
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
   /* leave when menu updates are locked */
   if (lock_menu_updates)
     return;
@@ -2771,11 +2933,10 @@ mousepad_window_action_line_numbers (GtkToggleAction *action,
   line_numbers = gtk_toggle_action_get_active (action);
 
   /* save as the last used line number setting */
-  g_object_set (G_OBJECT (window->preferences), "last-line-numbers", line_numbers, NULL);
+  g_object_set (G_OBJECT (window->preferences), "view-line-numbers", line_numbers, NULL);
 
   /* update the active document */
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_set_line_numbers (window->active, line_numbers);
+  mousepad_view_set_line_numbers (window->active->textview, line_numbers);
 }
 
 
@@ -2786,6 +2947,8 @@ mousepad_window_action_auto_indent (GtkToggleAction *action,
 {
   gboolean auto_indent;
 
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
   /* leave when menu updates are locked */
   if (lock_menu_updates)
     return;
@@ -2794,11 +2957,34 @@ mousepad_window_action_auto_indent (GtkToggleAction *action,
   auto_indent = gtk_toggle_action_get_active (action);
 
   /* save as the last auto indent mode */
-  g_object_set (G_OBJECT (window->preferences), "last-auto-indent", auto_indent, NULL);
+  g_object_set (G_OBJECT (window->preferences), "view-auto-indent", auto_indent, NULL);
+
+  /* update the active document */
+  mousepad_view_set_auto_indent (window->active->textview, auto_indent);
+}
+
+
+
+static void
+mousepad_window_action_insert_spaces (GtkToggleAction *action,
+                                      MousepadWindow  *window)
+{
+  gboolean insert_spaces;
+
+  _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+  /* leave when menu updates are locked */
+  if (lock_menu_updates)
+    return;
+
+  /* get the current state */
+  insert_spaces = gtk_toggle_action_get_active (action);
+
+  /* save as the last auto indent mode */
+  g_object_set (G_OBJECT (window->preferences), "view-insert-spaces", insert_spaces, NULL);
 
   /* update the active document */
-  if (G_LIKELY (window->active != NULL))
-    mousepad_document_set_auto_indent (window->active, auto_indent);
+  mousepad_view_set_insert_spaces (window->active->textview, insert_spaces);
 }
 
 
@@ -2899,22 +3085,3 @@ mousepad_window_delete_event (MousepadWindow *window,
   /* we will close the window when all the tabs are closed */
   return TRUE;
 }
-
-
-
-void
-mousepad_gtk_set_tooltip (GtkWidget   *widget,
-                          const gchar *tooltip)
-{
-  static GtkTooltips *tooltips = NULL;
-
-  _mousepad_return_if_fail (GTK_IS_WIDGET (widget));
-
-  /* allocate the shared tooltips on-demand */
-  if (G_UNLIKELY (tooltips == NULL))
-    tooltips = gtk_tooltips_new ();
-
-  /* setup the tooltip for the widget */
-  gtk_tooltips_set_tip (tooltips, widget, tooltip, NULL);
-}
-
diff --git a/mousepad/mousepad-window.h b/mousepad/mousepad-window.h
index 72f9bee..8bf4ff6 100644
--- a/mousepad/mousepad-window.h
+++ b/mousepad/mousepad-window.h
@@ -52,15 +52,13 @@ void            mousepad_window_add              (MousepadWindow   *window,
                                                   MousepadDocument *document);
 
 gboolean        mousepad_window_open_tab         (MousepadWindow  *window,
-                                                  const gchar     *filename);
+                                                  const gchar     *filename,
+                                                  const gchar     *encoding);
 
 gboolean        mousepad_window_open_files       (MousepadWindow  *window,
                                                   const gchar     *working_directory,
                                                   gchar          **filenames);
 
-void            mousepad_gtk_set_tooltip         (GtkWidget       *widget,
-                                                  const gchar     *string);
-
 G_END_DECLS
 
 #endif /* !__MOUSEPAD_WINDOW_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6a2bb24..db1522a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -6,15 +6,20 @@ mousepad/mousepad-application.c
 mousepad/mousepad-dbus.c
 mousepad/mousepad-dialogs.c
 mousepad/mousepad-document.c
-mousepad/mousepad-exo.c
+mousepad/mousepad-encoding-dialog.c
 mousepad/mousepad-file.c
 mousepad/mousepad-preferences.c
+mousepad/mousepad-replace-dialog.c
 mousepad/mousepad-search-bar.c
 mousepad/mousepad-statusbar.c
+mousepad/mousepad-undo.c
+mousepad/mousepad-util.c
 mousepad/mousepad-view.c
 mousepad/mousepad-window.c
 
+
+
 #
 # Desktop Files
 #
-Mousepad.desktop.in.in
\ No newline at end of file
+Mousepad.desktop.in.in
diff --git a/po/mousepad.pot b/po/mousepad.pot
index 5e51115..2467193 100644
--- a/po/mousepad.pot
+++ b/po/mousepad.pot
@@ -8,525 +8,580 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-04-04 20:31+0200\n"
+"POT-Creation-Date: 2007-10-16 21:41+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ../mousepad/main.c:53
+#: ../mousepad/main.c:51
 msgid "Do not register with the D-BUS session message bus"
 msgstr ""
 
-#: ../mousepad/main.c:54
+#: ../mousepad/main.c:52
 msgid "Quit a running Mousepad instance"
 msgstr ""
 
-#: ../mousepad/main.c:56
+#: ../mousepad/main.c:54
 msgid "Print version information and exit"
 msgstr ""
 
 #. default application name
-#: ../mousepad/main.c:78 ../Mousepad.desktop.in.in.h:1
+#: ../mousepad/main.c:75 ../Mousepad.desktop.in.in.h:1
 msgid "Mousepad"
 msgstr ""
 
-#. initialize Gtk+
-#: ../mousepad/main.c:90
+#. initialize gtk+
+#: ../mousepad/main.c:87
 msgid "[FILES...]"
 msgstr ""
 
-#. no error message, the GUI initialization failed
-#: ../mousepad/main.c:96
+#. no error message, the gui initialization failed
+#: ../mousepad/main.c:93
 msgid "Failed to open display."
 msgstr ""
 
-#: ../mousepad/main.c:113
+#: ../mousepad/main.c:110
 msgid "The Xfce development team. All rights reserved."
 msgstr ""
 
-#: ../mousepad/main.c:114
+#: ../mousepad/main.c:111
 #, c-format
 msgid "Please report bugs to <%s>."
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:62
+#: ../mousepad/mousepad-dialogs.c:41
 msgid "Mousepad is a fast text editor for the Xfce Desktop Environment."
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:63
-msgid "Copyright © 2004-2007 Xfce Development Team"
-msgstr ""
-
-#: ../mousepad/mousepad-dialogs.c:69
+#: ../mousepad/mousepad-dialogs.c:47
 msgid "translator-credits"
 msgstr ""
 
 #. build the dialog
-#: ../mousepad/mousepad-dialogs.c:116
+#: ../mousepad/mousepad-dialogs.c:94
 msgid "Jump To"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:130
+#: ../mousepad/mousepad-dialogs.c:108
 msgid "_Line number:"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:173
-msgid "Remove all entries from the document history?"
+#: ../mousepad/mousepad-dialogs.c:147
+msgid "Remove all entries from the documents history?"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:180
-msgid "Clear Document History"
+#: ../mousepad/mousepad-dialogs.c:152
+msgid "Clear Documents History"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:185
+#: ../mousepad/mousepad-dialogs.c:155
 msgid ""
-"Clearing the document history will permanently remove all currently listed "
+"Clearing the documents history will permanently remove all currently listed "
 "entries."
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:214
+#: ../mousepad/mousepad-dialogs.c:189
 msgid "Do you want to save the changes before closing?"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:217
+#: ../mousepad/mousepad-dialogs.c:192
 msgid "_Don't Save"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:225
+#: ../mousepad/mousepad-dialogs.c:200
 msgid "Save Changes"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:230
+#: ../mousepad/mousepad-dialogs.c:205
 msgid "If you don't save the document, all the changes will be lost."
 msgstr ""
 
-#. tell the user he cannot write to this file
-#: ../mousepad/mousepad-dialogs.c:261
-msgid "Permission denied"
-msgstr ""
-
-#: ../mousepad/mousepad-dialogs.c:283
-msgid "Save As"
-msgstr ""
-
-#: ../mousepad/mousepad-dialogs.c:324
+#: ../mousepad/mousepad-dialogs.c:229
 msgid ""
 "The file has been externally modified. Are you sure you want to save the "
 "file?"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:328
+#: ../mousepad/mousepad-dialogs.c:233
 #, c-format
 msgid "If you save the file, the external changes to \"%s\" will be lost."
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:333
+#: ../mousepad/mousepad-dialogs.c:238
 msgid "_Overwrite"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:336 ../mousepad/mousepad-dialogs.c:370
+#: ../mousepad/mousepad-dialogs.c:241 ../mousepad/mousepad-dialogs.c:275
 msgid "_Reload"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:361
+#: ../mousepad/mousepad-dialogs.c:266
 msgid "Do you want to save your changes before reloading?"
 msgstr ""
 
-#: ../mousepad/mousepad-dialogs.c:363
+#: ../mousepad/mousepad-dialogs.c:268
 msgid "If you reload the file, you changes will be lost."
 msgstr ""
 
-#: ../mousepad/mousepad-document.c:1044
+#: ../mousepad/mousepad-document.c:567
 msgid "Close this tab"
 msgstr ""
 
-#: ../mousepad/mousepad-document.c:1097
+#. create an unique untitled document name
+#: ../mousepad/mousepad-document.c:602
 msgid "Untitled"
 msgstr ""
 
-#: ../mousepad/mousepad-file.c:139
-#, c-format
-msgid "Failed to open \"%s\" for writing"
+#. create the header
+#: ../mousepad/mousepad-encoding-dialog.c:231
+msgid "The document was not UTF-8 valid"
 msgstr ""
 
-#: ../mousepad/mousepad-file.c:147
-#, c-format
-msgid "Failed to create new file \"%s\""
+#: ../mousepad/mousepad-encoding-dialog.c:232
+msgid "Please select an encoding below."
 msgstr ""
 
-#: ../mousepad/mousepad-file.c:160 ../mousepad/mousepad-file.c:381
-#, c-format
-msgid "You don't have permission to write to \"%s\""
+#. encoding radio buttons
+#: ../mousepad/mousepad-encoding-dialog.c:244
+msgid "Default (UTF-8)"
+msgstr ""
+
+#: ../mousepad/mousepad-encoding-dialog.c:250
+msgid "System"
+msgstr ""
+
+#: ../mousepad/mousepad-encoding-dialog.c:257
+msgid "Other:"
 msgstr ""
 
-#: ../mousepad/mousepad-file.c:169
+#: ../mousepad/mousepad-file.c:338
 #, c-format
-msgid "Failed to truncate \"%s\" before writing"
+msgid "Unknown line ending detected (%s)"
 msgstr ""
 
-#: ../mousepad/mousepad-file.c:185
+#: ../mousepad/mousepad-file.c:346
 #, c-format
-msgid "Failed to write data to \"%s\""
+msgid "Converted string is not UTF-8 valid"
 msgstr ""
 
-#: ../mousepad/mousepad-file.c:266 ../mousepad/mousepad-file.c:361
+#: ../mousepad/mousepad-file.c:527
 #, c-format
-msgid "Failed to open \"%s\" for reading"
+msgid "The file \"%s\" you've tried to reload does not exist anymore"
 msgstr ""
 
-#: ../mousepad/mousepad-file.c:279
+#: ../mousepad/mousepad-file.c:564
 #, c-format
-msgid "You are not allowed to read \"%s\""
+msgid "Failed to read the status of \"%s\""
+msgstr ""
+
+#: ../mousepad/mousepad-preferences.c:502
+msgid "Failed to load the preferences."
+msgstr ""
+
+#: ../mousepad/mousepad-preferences.c:598
+msgid "Failed to store the preferences."
+msgstr ""
+
+#. set dialog properties
+#: ../mousepad/mousepad-replace-dialog.c:191
+msgid "Replace"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:198
+#: ../mousepad/mousepad-replace-dialog.c:560
+msgid "_Replace"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:216
+msgid "_Search for:"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:237
+msgid "Replace _with:"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:257
+msgid "Search _direction:"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:266
+msgid "Up"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:267
+msgid "Down"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:268
+msgid "Both"
+msgstr ""
+
+#. case sensitive
+#: ../mousepad/mousepad-replace-dialog.c:277
+msgid "Case sensi_tive"
+msgstr ""
+
+#. match whole word
+#: ../mousepad/mousepad-replace-dialog.c:284
+msgid "_Match whole word"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:295
+msgid "Replace _all in:"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:302
+msgid "Selection"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:303
+msgid "Document"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:304
+msgid "All Documents"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:465
+msgid "occurence"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:465
+msgid "occurences"
+msgstr ""
+
+#: ../mousepad/mousepad-replace-dialog.c:560
+msgid "_Replace All"
 msgstr ""
 
-#: ../mousepad/mousepad-search-bar.c:207
+#: ../mousepad/mousepad-search-bar.c:211
 msgid "Fi_nd:"
 msgstr ""
 
-#: ../mousepad/mousepad-search-bar.c:228
+#: ../mousepad/mousepad-search-bar.c:231
 msgid "_Next"
 msgstr ""
 
-#: ../mousepad/mousepad-search-bar.c:240
+#: ../mousepad/mousepad-search-bar.c:242
 msgid "_Previous"
 msgstr ""
 
-#: ../mousepad/mousepad-search-bar.c:251
+#: ../mousepad/mousepad-search-bar.c:253
 msgid "Highlight _All"
 msgstr ""
 
-#: ../mousepad/mousepad-search-bar.c:264 ../mousepad/mousepad-search-bar.c:270
+#: ../mousepad/mousepad-search-bar.c:266 ../mousepad/mousepad-search-bar.c:273
 msgid "Mat_ch Case"
 msgstr ""
 
-#: ../mousepad/mousepad-statusbar.c:170
-#, c-format
-msgid "Line %d Col %d"
+#: ../mousepad/mousepad-statusbar.c:142
+msgid "Toggle the overwrite mode"
 msgstr ""
 
-#: ../mousepad/mousepad-statusbar.c:183
+#. overwrite label
+#: ../mousepad/mousepad-statusbar.c:147
 msgid "OVR"
 msgstr ""
 
-#: ../mousepad/mousepad-statusbar.c:183
-msgid "INS"
+#. create printable string
+#: ../mousepad/mousepad-statusbar.c:186
+#, c-format
+msgid "Line %d Col %d"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:264
+#: ../mousepad/mousepad-window.c:313
 msgid "_File"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:265
-msgid "New _Tab"
+#: ../mousepad/mousepad-window.c:314
+msgid "New Documen_t"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:265
+#: ../mousepad/mousepad-window.c:314
 msgid "Create a new document"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:266
+#: ../mousepad/mousepad-window.c:315
 msgid "_New Window"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:266
+#: ../mousepad/mousepad-window.c:315
 msgid "Create a new document in a new window"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:267
+#: ../mousepad/mousepad-window.c:316
 msgid "_Open File"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:267
+#: ../mousepad/mousepad-window.c:316
 msgid "Open a file"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:268
+#: ../mousepad/mousepad-window.c:317
 msgid "Open _Recent"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:269
+#: ../mousepad/mousepad-window.c:318
 msgid "No items found"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:270
+#: ../mousepad/mousepad-window.c:319
 msgid "Clear _History"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:270
+#: ../mousepad/mousepad-window.c:319
 msgid "Clear the recently used files history"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:271
-msgid "_Save"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:271
+#: ../mousepad/mousepad-window.c:320
 msgid "Save the current file"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:272
-msgid "Save _As"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:272
+#: ../mousepad/mousepad-window.c:321
 msgid "Save current document as another file"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:273
+#: ../mousepad/mousepad-window.c:322
 msgid "Re_load"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:273
+#: ../mousepad/mousepad-window.c:322
 msgid "Reload this document."
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:274
-msgid "C_lose Tab"
+#: ../mousepad/mousepad-window.c:323
+msgid "Prin the current page"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:274
-msgid "Close the current file"
+#: ../mousepad/mousepad-window.c:324
+msgid "_Detach Tab"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:275
-msgid "_Close Window"
+#: ../mousepad/mousepad-window.c:324
+msgid "Move the current document to a new window"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:275
-msgid "Quit the program"
+#: ../mousepad/mousepad-window.c:325
+msgid "C_lose Tab"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:276
-msgid "Close _All Windows"
+#: ../mousepad/mousepad-window.c:325
+msgid "Close the current file"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:276
-msgid "Close all Mousepad windows"
+#: ../mousepad/mousepad-window.c:326
+msgid "_Close Window"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:278
-msgid "_Edit"
+#: ../mousepad/mousepad-window.c:326
+msgid "Quit the program"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:279
-msgid "_Undo"
+#: ../mousepad/mousepad-window.c:328
+msgid "_Edit"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:279
+#: ../mousepad/mousepad-window.c:329
 msgid "Undo the last action"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:280
-msgid "_Redo"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:280
+#: ../mousepad/mousepad-window.c:330
 msgid "Redo the last undone action"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:281
-msgid "Cu_t"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:281
+#: ../mousepad/mousepad-window.c:331
 msgid "Cut the selection"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:282
-msgid "_Copy"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:282
+#: ../mousepad/mousepad-window.c:332
 msgid "Copy the selection"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:283
-msgid "_Paste"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:283
+#: ../mousepad/mousepad-window.c:333
 msgid "Paste the clipboard"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:284
-msgid "_Delete"
+#: ../mousepad/mousepad-window.c:334
+msgid "Paste _Column"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:284
-msgid "Delete the selected text"
+#: ../mousepad/mousepad-window.c:334
+msgid "Paste the clipboard text in a clumn"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:285
-msgid "Select _All"
+#: ../mousepad/mousepad-window.c:335
+msgid "Delete the selected text"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:285
+#: ../mousepad/mousepad-window.c:336
 msgid "Select the entire document"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:287
+#: ../mousepad/mousepad-window.c:338
 msgid "_Search"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:288
-msgid "_Find"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:288
+#: ../mousepad/mousepad-window.c:339
 msgid "Search for text"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:289
+#: ../mousepad/mousepad-window.c:340
 msgid "Find _Next"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:289
+#: ../mousepad/mousepad-window.c:340
 msgid "Search forwards for the same text"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:290
+#: ../mousepad/mousepad-window.c:341
 msgid "Find _Previous"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:290
+#: ../mousepad/mousepad-window.c:341
 msgid "Search backwards for the same text"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:291
-msgid "_Replace"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:291
+#: ../mousepad/mousepad-window.c:342
 msgid "Search for and replace text"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:292
-msgid "_Jump To"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:292
+#: ../mousepad/mousepad-window.c:343
 msgid "Go to a specific line"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:294
+#: ../mousepad/mousepad-window.c:345
 msgid "_View"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:295
-msgid "_Font"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:295
+#: ../mousepad/mousepad-window.c:346
 msgid "Change the editor font"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:297
+#: ../mousepad/mousepad-window.c:348
 msgid "_Document"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:299
+#: ../mousepad/mousepad-window.c:350
 msgid "_Go"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:300
-msgid "Back"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:300
+#: ../mousepad/mousepad-window.c:351
 msgid "Select the previous tab"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:301
-msgid "Forward"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:301
+#: ../mousepad/mousepad-window.c:352
 msgid "Select the next tab"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:303
+#: ../mousepad/mousepad-window.c:354
 msgid "_Help"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:304
-msgid "_About"
-msgstr ""
-
-#: ../mousepad/mousepad-window.c:304
+#: ../mousepad/mousepad-window.c:355
 msgid "About this application"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:309
+#: ../mousepad/mousepad-window.c:360
 msgid "_Statusbar"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:309
+#: ../mousepad/mousepad-window.c:360
 msgid "Change the visibility of the statusbar"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:310
+#: ../mousepad/mousepad-window.c:361
 msgid "_Word Wrap"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:310
+#: ../mousepad/mousepad-window.c:361
 msgid "Toggle breaking lines in between words"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:311
+#: ../mousepad/mousepad-window.c:362
 msgid "_Line Numbers"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:312
+#: ../mousepad/mousepad-window.c:362
+msgid "Show line numbers"
+msgstr ""
+
+#: ../mousepad/mousepad-window.c:363
 msgid "_Auto Indent"
 msgstr ""
 
-#. add the label with the root warning
-#: ../mousepad/mousepad-window.c:468
-msgid "Warning, you are using the root account, you may harm your system."
+#: ../mousepad/mousepad-window.c:363
+msgid "Auto indent a new line"
 msgstr ""
 
-#. show the warning
-#: ../mousepad/mousepad-window.c:775
-msgid "Failed to open file"
+#: ../mousepad/mousepad-window.c:364
+msgid "_Insert Spaces"
 msgstr ""
 
-#. the warnings message for save as
-#: ../mousepad/mousepad-window.c:867
-msgid "Failed to save the document as another file"
+#: ../mousepad/mousepad-window.c:364
+msgid "Insert spaces when the tab button is pressed"
 msgstr ""
 
-#. the warning message for save
-#: ../mousepad/mousepad-window.c:896
-msgid "Failed to save the document"
+#. add the label with the root warning
+#: ../mousepad/mousepad-window.c:556
+msgid "Warning, you are using the root account, you may harm your system."
 msgstr ""
 
-#. the warning message for save
-#: ../mousepad/mousepad-window.c:904 ../mousepad/mousepad-window.c:1958
-msgid "Failed to reload the document"
+#. show the warning
+#. show the warning and cleanup
+#: ../mousepad/mousepad-window.c:935 ../mousepad/mousepad-window.c:2282
+msgid "Failed to open file"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:1057
+#: ../mousepad/mousepad-window.c:1122
 msgid "Read Only"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:1618
+#. build description
+#. set text offset
+#: ../mousepad/mousepad-window.c:1726 ../mousepad/mousepad-window.c:2266
+msgid "Encoding"
+msgstr ""
+
+#: ../mousepad/mousepad-window.c:1860
 #, c-format
 msgid "Open '%s'"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:1728
-msgid "Failed to remove an item from the recent history"
+#: ../mousepad/mousepad-window.c:1970
+msgid "Failed to remove an item from the documents history"
 msgstr ""
 
 #. create new chooser dialog
-#: ../mousepad/mousepad-window.c:1776
+#: ../mousepad/mousepad-window.c:2175
 msgid "Open File"
 msgstr ""
 
-#: ../mousepad/mousepad-window.c:2228
+#: ../mousepad/mousepad-window.c:2279
+#, c-format
+msgid ""
+"Failed to open \"%s\" for reading. It will be removed from the document "
+"history"
+msgstr ""
+
+#. show the warning
+#: ../mousepad/mousepad-window.c:2349
+msgid "Failed to save the document"
+msgstr ""
+
+#. create the dialog
+#: ../mousepad/mousepad-window.c:2373
+msgid "Save As"
+msgstr ""
+
+#. show the warning
+#: ../mousepad/mousepad-window.c:2441
+msgid "Failed to reload the document"
+msgstr ""
+
+#: ../mousepad/mousepad-window.c:2809
 msgid "Choose Mousepad Font"
 msgstr ""
 


More information about the Xfce4-commits mailing list