diff --git a/ChangeLog b/ChangeLog
index 5ba9ea9d4990c15a522b8d0f754f494f2bfa8905..081f1a2b3950141ccf3795a88fdf6381505a4ea9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,74 @@
+2005-07-18	Benedikt Meurer <benny@xfce.org>
+
+	* thunar-vfs/thunar-vfs-job-listdir.c(thunar_vfs_job_listdir_execute):
+	  Always ignore the "." and ".." entries for directories.
+	* thunar/thunar-location-bar.{c,h}: Add virtual method accept_focus(),
+	  which tries to transfer keyboard focus to the location bar if it
+	  provides a way for the user to enter the location as text. Else
+	  the method returns FALSE and the window will open a separate dialog.
+	* thunar/thunar-location-buttons.c, thunar/thunar-location-entry.c:
+	  Implement the accept_focus() method on the ThunarLocationBar
+	  interface appropriately.
+	* thunar/thunar-window-ui.xml, thunar/thunar-window.c: Make the
+	  location bar configurable using the action system.
+
+2005-07-17	Benedikt Meurer <benny@xfce.org>
+
+	* thunar/thunar-path-entry.c: Do not handle the focus-out-event here,
+	  as it's better to handle it in the upper layers.
+	* thunar/thunar-path-entry.c(thunar_path_entry_set_current_file): Set
+	  the cursor to the end and queue a redraw on the whole widget.
+	* thunar/thunar-window.c: Include <thunar/thunar-location-entry.h>.
+	* thunar/thunar-location-entry.{c,h}, thunar/Makefile.am: Add
+	  ThunarLocationEntry, which implements ThunarLocationBar using a
+	  ThunarPathEntry widget.
+
+2005-07-17	Benedikt Meurer <benny@xfce.org>
+
+	* thunar/thunar-path-entry.{c,h}: Add ThunarPathEntry class, which
+	  implements a widget to which the user can enter paths. The auto
+	  completion support is not yet provided.
+	* thunar/thunar-location-dialog.{c,h}: Add ThunarLocationDialog class,
+	  which provides a "Open Location"-dialog similar to the one found in
+	  GtkFileChooser.
+	* thunar/Makefile.am: Add new classes to the build framework.
+	* thunar/thunar-window-ui.xml, thunar/thunar-window.c: Add "Open
+	  Location" action to the window, which will bring up a
+	  ThunarLocationDialog.
+
+2005-07-17	Benedikt Meurer <benny@xfce.org>
+
+	* configure.in.in: Use "thunar-threaded" instead of "Thunar-threaded".
+	* tests/test-thunar-vfs-volume-bsd.c(main): Get this test working again.
+
+2005-07-17	Benedikt Meurer <benny@xfce.org>
+
+	* TODO: Add note to based the ThunarVfsJob communication on the GSignal
+	  mechanism.
+	* configure.in.in: Set tarname to be "Thunar-threaded".
+	* thunar-vfs/thunar-vfs-info.{c,h}: Let thunar_vfs_info_list_free()
+	  return NULL.
+	* thunar-vfs/thunar-vfs-job-listdir.c: Invoke the callback every two
+	  seconds, so for large directories, the user does not need to wait for
+	  the whole folder to be loaded. Also sort the item names prior to
+	  loading the infos.
+	* thunar/thunar-local-folder.c: Support partial loading, as implemented
+	  for ThunarVfsJobListdir.
+
+2005-07-17	Benedikt Meurer <benny@xfce.org>
+
+	* thunar-vfs/thunar-vfs-volume-sysv.c: Fix compile warning with wrong
+	  parameters to thunar_vfs_volume_manager_sysv_get_volume_by_info().
+	  This fixes bug #1083.
+
+2005-07-17	Benedikt Meurer <benny@xfce.org>
+
+	* thunar-vfs/xdgmime/: Import the XDG mime implementation with the
+	  patches from libexo.
+	* thunar-vfs/: The ThunarVfsInfo framework is now thread-safe, using
+	  the ThunarVfsMime framework provided by ThunarVFS.
+	* thunar/: Turn Thunar into a threaded application.
+
 2005-07-16	Benedikt Meurer <benny@xfce.org>
 
 	* thunar/thunar-folder.{c,h}: Add a "files-removed" signal, which can
diff --git a/TODO b/TODO
index 5900ca3b63f340fc2bbeb77af50c89fe6efa5d2c..69045ccc8289f5439ef73676813d5a5ae135e242 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,8 @@
 Important for Thunar 1.0
 ========================
 
+ - Use GObject signals for the ThunarVfsJob communication.
+
  - In thunar_standard_view_selection_changed(), the "Cut file(s)" action
    should only be sensitive if the current folder is writable.
 
diff --git a/autogen.sh b/autogen.sh
index a23f9fea262e4717ed1ee1042f1b9f4123e8b9f9..6968b656bc6c88631a2963ee6fc2e3003533d519 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# $Id: autogen.sh,v 1.7 2005/01/26 22:07:26 benny Exp $
+# $Id$
 #
 # Copyright (c) 2002-2005
 #         The Thunar development team. All rights reserved.
diff --git a/configure.in.in b/configure.in.in
index 41426884bb1471032caf4dadc42712436a452b48..db303c2fcbf8fd408423c04d3792abae3b481b4a 100644
--- a/configure.in.in
+++ b/configure.in.in
@@ -20,7 +20,7 @@ AC_COPYRIGHT([Copyright (c) 2004-2005
         The Thunar development team. All rights reserved.
         
 Written for Thunar by Benedikt Meurer <benny@xfce.org>.])
-AC_INIT([Thunar], [thunar_version()], [benny@xfce.org])
+AC_INIT([Thunar], [thunar_version()], [benny@xfce.org], [thunar-threaded])
 AC_PREREQ([2.50])
 AC_CANONICAL_TARGET()
 AC_REVISION([$Id$])
@@ -41,12 +41,13 @@ AC_PROG_INSTALL()
 AC_PROG_LIBTOOL()
 
 dnl Check for standard headers
-AC_CHECK_HEADERS([errno.h fcntl.h fstab.h grp.h memory.h pwd.h \
+AC_CHECK_HEADERS([dirent.h errno.h fcntl.h fstab.h grp.h locale.h memory.h \
+                  pwd.h \
                   stdlib.h string.h sys/cdio.h sys/event.h \
                   sys/mount.h sys/stat.h sys/time.h sys/param.h time.h])
 
 dnl Check for standard functions
-AC_CHECK_FUNCS([kqueue localtime_r setgroupent setpassent])
+AC_CHECK_FUNCS([kqueue localtime_r readdir_r setgroupent setpassent])
 
 dnl Check for required packages
 XDT_CHECK_PACKAGE([EXO], [exo-0.3], [0.3.1])
@@ -83,4 +84,5 @@ tests/Makefile
 tests/data/Makefile
 thunar/Makefile
 thunar-vfs/Makefile
+thunar-vfs/xdgmime/Makefile
 ])
diff --git a/tests/test-thunar-vfs-volume-bsd.c b/tests/test-thunar-vfs-volume-bsd.c
index bca32e748d43b1e4120a711f346ce71b98e259c5..1bf4218091e2101c85406b116b03e42310ae211b 100644
--- a/tests/test-thunar-vfs-volume-bsd.c
+++ b/tests/test-thunar-vfs-volume-bsd.c
@@ -47,17 +47,17 @@ main (int argc, char **argv)
 
   volume = g_list_nth_data (volumes, 0);
   g_assert (THUNAR_VFS_IS_VOLUME (volume));
-  g_assert (!thunar_vfs_volume_get_mount_status (volume));
+  g_assert (!thunar_vfs_volume_is_mounted (volume));
   g_assert (THUNAR_VFS_IS_URI (thunar_vfs_volume_get_mount_point (volume)));
 
   volume = g_list_nth_data (volumes, 1);
   g_assert (THUNAR_VFS_IS_VOLUME (volume));
-  g_assert (!thunar_vfs_volume_get_mount_status (volume));
+  g_assert (!thunar_vfs_volume_is_mounted (volume));
   g_assert (THUNAR_VFS_IS_URI (thunar_vfs_volume_get_mount_point (volume)));
 
   volume = g_list_nth_data (volumes, 2);
   g_assert (THUNAR_VFS_IS_VOLUME (volume));
-  g_assert (!thunar_vfs_volume_get_mount_status (volume));
+  g_assert (!thunar_vfs_volume_is_mounted (volume));
   g_assert (THUNAR_VFS_IS_URI (thunar_vfs_volume_get_mount_point (volume)));
 
   g_object_unref (G_OBJECT (manager));
diff --git a/thunar-vfs/Makefile.am b/thunar-vfs/Makefile.am
index 78f760f54f9720c017813cd32655bb6da4868b8d..9762e8cb86dd9edb883a65d4f55f12a63d92c895 100644
--- a/thunar-vfs/Makefile.am
+++ b/thunar-vfs/Makefile.am
@@ -1,10 +1,14 @@
 # $Id$
 
+SUBDIRS =								\
+	xdgmime
+
 INCLUDES =								\
 	-I$(top_srcdir)							\
 	-DEXO_API_SUBJECT_TO_CHANGE					\
 	-DEXO_DISABLE_DEPRECATED					\
-	-DG_LOG_DOMAIN=\"thunar-vfs\"
+	-DG_LOG_DOMAIN=\"thunar-vfs\"					\
+	-DXDG_PREFIX=_thunar_vfs_xdg
 
 noinst_LTLIBRARIES =							\
 	libthunar-vfs.la
@@ -15,6 +19,9 @@ libthunar_vfs_built_sources =						\
 
 libthunar_vfs_headers =							\
 	thunar-vfs-info.h						\
+	thunar-vfs-job.h						\
+	thunar-vfs-job-listdir.h					\
+	thunar-vfs-mime.h						\
 	thunar-vfs-monitor.h						\
 	thunar-vfs-trash.h						\
 	thunar-vfs-user.h						\
@@ -27,6 +34,11 @@ libthunar_vfs_la_SOURCES =						\
 	$(libthunar_vfs_built_sources)					\
 	$(libthunar_vfs_headers)					\
 	thunar-vfs-info.c						\
+	thunar-vfs-job.c						\
+	thunar-vfs-job-listdir.c					\
+	thunar-vfs-mime.c						\
+	thunar-vfs-mime-parser.h					\
+	thunar-vfs-mime-parser.c					\
 	thunar-vfs-monitor.c						\
 	thunar-vfs-trash.c						\
 	thunar-vfs-user.c						\
@@ -34,14 +46,21 @@ libthunar_vfs_la_SOURCES =						\
 	thunar-vfs-uri.c						\
 	thunar-vfs-volume.c						\
 	thunar-vfs-volume-impl.c					\
-	thunar-vfs-volume-impl.h
+	thunar-vfs-volume-impl.h					\
+	thunar-vfs.c
 
 libthunar_vfs_la_CFLAGS =						\
 	$(EXO_CFLAGS)							\
 	$(GTHREAD_CFLAGS)
 
 libthunar_vfs_la_LDFLAGS =						\
-	-no-undefined							\
+	-no-undefined
+
+libthunar_vfs_la_DEPENDENCIES =						\
+	$(top_builddir)/thunar-vfs/xdgmime/libxdgmime.la
+
+libthunar_vfs_la_LIBADD =						\
+	$(top_builddir)/thunar-vfs/xdgmime/libxdgmime.la		\
 	$(EXO_LIBS)							\
 	$(GTHREADS_LIBS)
 
diff --git a/thunar-vfs/thunar-vfs-info.c b/thunar-vfs/thunar-vfs-info.c
index 39c425454cb308cb4c7594fc22dbde394a0ca340..110ef3555a827162186986e1b153d412027786dc 100644
--- a/thunar-vfs/thunar-vfs-info.c
+++ b/thunar-vfs/thunar-vfs-info.c
@@ -40,76 +40,78 @@
 
 
 
-static ExoMimeDatabase *mime_database = NULL;
-
-
-
-/**
- * thunar_vfs_info_query:
- * @info  : pointer to an uninitialized #ThunarVfsInfo object.
- * @uri   : an #ThunarVfsURI instance.
- * @error : return location for errors.
- *
- * Return value: %TRUE if the operation succeed, else %FALSE.
- **/
-gboolean
-thunar_vfs_info_query (ThunarVfsInfo  *info,
-                       ThunarVfsURI   *uri,
-                       GError        **error)
-{
-  g_return_val_if_fail (info != NULL, FALSE);
-  g_return_val_if_fail (info->uri == NULL, FALSE);
-  g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), FALSE);
-
-  info->uri = thunar_vfs_uri_ref (uri);
-
-  if (thunar_vfs_info_update (info, error) == THUNAR_VFS_INFO_RESULT_ERROR)
-    {
-      thunar_vfs_uri_unref (uri);
-      info->uri = NULL;
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-
-
 /**
- * thunar_vfs_info_update:
- * @info  :
- * @error :
+ * thunar_vfs_info_new_for_uri:
+ * @uri   : the #ThunarVfsURI of the file whose info should be queried.
+ * @error : return location for errors or %NULL.
  *
- * FIXME
+ * Queries the #ThunarVfsInfo for the given @uri. Returns the
+ * #ThunarVfsInfo if the operation is successfull, else %NULL.
+ * In the latter case, @error will be set to point to a #GError
+ * describing the cause of the failure.
  *
- * Return value:
+ * Return value: the #ThunarVfsInfo for @uri or %NULL.
  **/
-ThunarVfsInfoResult
-thunar_vfs_info_update (ThunarVfsInfo *info,
-                        GError       **error)
+ThunarVfsInfo*
+thunar_vfs_info_new_for_uri (ThunarVfsURI *uri,
+                             GError      **error)
 {
-  const gchar *path;
-  struct stat  lsb;
-  struct stat  sb;
+  ThunarVfsInfo *info;
+  const gchar   *path;
+  struct stat    lsb;
+  struct stat    sb;
+
+  g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  path = thunar_vfs_uri_get_path (info->uri);
+  path = thunar_vfs_uri_get_path (uri);
 
   if (G_UNLIKELY (lstat (path, &lsb) < 0))
     {
       g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                    "Failed to stat file `%s': %s", path, g_strerror (errno));
-      return THUNAR_VFS_INFO_RESULT_ERROR;
+      return NULL;
     }
 
+  info = g_new (ThunarVfsInfo, 1);
+  info->uri = thunar_vfs_uri_ref (uri);
+  info->ref_count = 1;
+
   if (G_LIKELY (!S_ISLNK (lsb.st_mode)))
     {
-      if (info->ctime != lsb.st_ctime
-          || info->device != lsb.st_dev
-          || info->inode != lsb.st_ino)
+      info->type = (lsb.st_mode & S_IFMT) >> 12;
+      info->mode = lsb.st_mode & 07777;
+      info->flags = THUNAR_VFS_FILE_FLAGS_NONE;
+      info->uid = lsb.st_uid;
+      info->gid = lsb.st_gid;
+      info->size = lsb.st_size;
+      info->atime = lsb.st_atime;
+      info->ctime = lsb.st_ctime;
+      info->mtime = lsb.st_mtime;
+      info->inode = lsb.st_ino;
+      info->device = lsb.st_dev;
+    }
+  else
+    {
+      if (stat (path, &sb) == 0)
+        {
+          info->type = (sb.st_mode & S_IFMT) >> 12;
+          info->mode = sb.st_mode & 07777;
+          info->flags = THUNAR_VFS_FILE_FLAGS_SYMLINK;
+          info->uid = sb.st_uid;
+          info->gid = sb.st_gid;
+          info->size = sb.st_size;
+          info->atime = sb.st_atime;
+          info->ctime = sb.st_ctime;
+          info->mtime = sb.st_mtime;
+          info->inode = sb.st_ino;
+          info->device = sb.st_dev;
+        }
+      else
         {
-          info->type = (lsb.st_mode & S_IFMT) >> 12;
+          info->type = THUNAR_VFS_FILE_TYPE_SYMLINK;
           info->mode = lsb.st_mode & 07777;
-          info->flags = THUNAR_VFS_FILE_FLAGS_NONE;
+          info->flags = THUNAR_VFS_FILE_FLAGS_SYMLINK;
           info->uid = lsb.st_uid;
           info->gid = lsb.st_gid;
           info->size = lsb.st_size;
@@ -118,114 +120,155 @@ thunar_vfs_info_update (ThunarVfsInfo *info,
           info->mtime = lsb.st_mtime;
           info->inode = lsb.st_ino;
           info->device = lsb.st_dev;
-
-          return THUNAR_VFS_INFO_RESULT_CHANGED;
         }
     }
-  else
+
+  switch (info->type)
     {
-      if (stat (path, &sb) == 0)
-        {
-          /* ok, link target is present */
-          if (info->ctime != sb.st_ctime
-              || info->device != sb.st_dev
-              || info->inode != sb.st_ino)
-            {
-              info->type = (sb.st_mode & S_IFMT) >> 12;
-              info->mode = sb.st_mode & 07777;
-              info->flags = THUNAR_VFS_FILE_FLAGS_SYMLINK;
-              info->uid = sb.st_uid;
-              info->gid = sb.st_gid;
-              info->size = sb.st_size;
-              info->atime = sb.st_atime;
-              info->ctime = sb.st_ctime;
-              info->mtime = sb.st_mtime;
-              info->inode = sb.st_ino;
-              info->device = sb.st_dev;
-          
-              return THUNAR_VFS_INFO_RESULT_CHANGED;
-            }
-        }
-      else
-        {
-          /* we have a broken symlink */
-          if (info->type != THUNAR_VFS_FILE_TYPE_SYMLINK
-              || info->ctime != lsb.st_ctime
-              || info->device != lsb.st_dev
-              || info->inode != lsb.st_ino)
-            {
-              info->type = THUNAR_VFS_FILE_TYPE_SYMLINK;
-              info->mode = lsb.st_mode & 07777;
-              info->flags = THUNAR_VFS_FILE_FLAGS_SYMLINK;
-              info->uid = lsb.st_uid;
-              info->gid = lsb.st_gid;
-              info->size = lsb.st_size;
-              info->atime = lsb.st_atime;
-              info->ctime = lsb.st_ctime;
-              info->mtime = lsb.st_mtime;
-              info->inode = lsb.st_ino;
-              info->device = lsb.st_dev;
-
-              return THUNAR_VFS_INFO_RESULT_CHANGED;
-            }
-        }
+    case THUNAR_VFS_FILE_TYPE_SOCKET:
+      info->mime_info = thunar_vfs_mime_info_get ("inode/socket");
+      break;
+
+    case THUNAR_VFS_FILE_TYPE_SYMLINK:
+      info->mime_info = thunar_vfs_mime_info_get ("inode/symlink");
+      break;
+
+    case THUNAR_VFS_FILE_TYPE_BLOCKDEV:
+      info->mime_info = thunar_vfs_mime_info_get ("inode/blockdevice");
+      break;
+
+    case THUNAR_VFS_FILE_TYPE_DIRECTORY:
+      info->mime_info = thunar_vfs_mime_info_get ("inode/directory");
+      break;
+
+    case THUNAR_VFS_FILE_TYPE_CHARDEV:
+      info->mime_info = thunar_vfs_mime_info_get ("inode/chardevice");
+      break;
+
+    case THUNAR_VFS_FILE_TYPE_FIFO:
+      info->mime_info = thunar_vfs_mime_info_get ("inode/fifo");
+      break;
+
+    case THUNAR_VFS_FILE_TYPE_REGULAR:
+      info->mime_info = thunar_vfs_mime_info_get_for_file (path);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
     }
 
-  return THUNAR_VFS_INFO_RESULT_NOCHANGE;
+  return info;
 }
 
 
 
 /**
- * thunar_vfs_info_get_mime_info:
- * @info : pointer to a valid #ThunarVfsInfo object.
+ * thunar_vfs_info_ref:
+ * @info : a #ThunarVfsInfo.
  *
- * Determines the appropriate MIME type for @info. The
- * caller is responsible for freeing the returned object
- * using #g_object_unref() once it's no longer needed.
+ * Increments the reference count on @info by 1 and
+ * returns a pointer to @info.
  *
- * Return value: the #ExoMimeInfo instance for @info.
+ * Return value: a pointer to @info.
  **/
-ExoMimeInfo*
-thunar_vfs_info_get_mime_info (ThunarVfsInfo *info)
+ThunarVfsInfo*
+thunar_vfs_info_ref (ThunarVfsInfo *info)
 {
-  const gchar *path;
-
-  g_return_val_if_fail (info != NULL, NULL);
-  g_return_val_if_fail (THUNAR_VFS_IS_URI (info->uri), NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
 
-  if (G_UNLIKELY (mime_database == NULL))
-    mime_database = exo_mime_database_get_default ();
+  g_atomic_int_inc (&info->ref_count);
 
-  switch (info->type)
-    {
-    case THUNAR_VFS_FILE_TYPE_SOCKET:
-      return exo_mime_database_get_info (mime_database, "inode/socket");
+  return info;
+}
 
-    case THUNAR_VFS_FILE_TYPE_SYMLINK:
-      return exo_mime_database_get_info (mime_database, "inode/symlink");
 
-    case THUNAR_VFS_FILE_TYPE_BLOCKDEV:
-      return exo_mime_database_get_info (mime_database, "inode/blockdevice");
 
-    case THUNAR_VFS_FILE_TYPE_DIRECTORY:
-      return exo_mime_database_get_info (mime_database, "inode/directory");
+/**
+ * thunar_vfs_info_unref:
+ * @info : a #ThunarVfsInfo.
+ *
+ * Decrements the reference count on @info by 1 and if
+ * the reference count drops to zero as a result of this
+ * operation, the @info will be freed completely.
+ **/
+void
+thunar_vfs_info_unref (ThunarVfsInfo *info)
+{
+  g_return_if_fail (info != NULL);
+  g_return_if_fail (info->ref_count > 0);
 
-    case THUNAR_VFS_FILE_TYPE_CHARDEV:
-      return exo_mime_database_get_info (mime_database, "inode/chardevice");
+  if (g_atomic_int_dec_and_test (&info->ref_count))
+    {
+      thunar_vfs_mime_info_unref (info->mime_info);
+      thunar_vfs_uri_unref (info->uri);
 
-    case THUNAR_VFS_FILE_TYPE_FIFO:
-      return exo_mime_database_get_info (mime_database, "inode/fifo");
+#ifndef G_DISABLE_CHECKS
+      memset (info, 0xaa, sizeof (*info));
+#endif
 
-    default:
-      path = thunar_vfs_uri_get_path (THUNAR_VFS_URI (info->uri));
-      return exo_mime_database_get_info_for_file (mime_database, path);
+      g_free (info);
     }
+}
 
-  g_assert_not_reached ();
-  return NULL;
+
+
+/**
+ * thunar_vfs_info_matches:
+ * @a : a #ThunarVfsInfo.
+ * @b : a #ThunarVfsInfo.
+ *
+ * Checks whether @a and @b refer to the same file
+ * and share the same properties.
+ *
+ * Return value: %TRUE if @a and @b match.
+ **/
+gboolean
+thunar_vfs_info_matches (const ThunarVfsInfo *a,
+                         const ThunarVfsInfo *b)
+{
+  g_return_val_if_fail (a != NULL, FALSE);
+  g_return_val_if_fail (b != NULL, FALSE);
+  g_return_val_if_fail (a->ref_count > 0, FALSE);
+  g_return_val_if_fail (b->ref_count > 0, FALSE);
+
+  return thunar_vfs_uri_equal (a->uri, b->uri)
+      && a->type == b->type
+      && a->mode == b->mode
+      && a->flags == b->flags
+      && a->uid == b->uid
+      && a->gid == b->gid
+      && a->size == b->size
+      && a->atime == b->atime
+      && a->mtime == b->mtime
+      && a->ctime == b->ctime
+      && a->inode == b->inode
+      && a->device == b->device
+      && a->mime_info == b->mime_info;
 }
 
 
 
+/**
+ * thunar_vfs_info_list_free:
+ * @info_list : a list #ThunarVfsInfo<!---->s.
+ *
+ * Unrefs all #ThunarVfsInfo<!---->s in @info_list and
+ * frees the list itself.
+ *
+ * This method always returns %NULL for the convenience of
+ * being able to do:
+ * <informalexample><programlisting>
+ * info_list = thunar_vfs_info_list_free (info_list);
+ * </programlisting></informalexample>
+ *
+ * Return value: the empty list (%NULL).
+ **/
+GSList*
+thunar_vfs_info_list_free (GSList *info_list)
+{
+  g_slist_foreach (info_list, (GFunc) thunar_vfs_info_unref, NULL);
+  g_slist_free (info_list);
+  return NULL;
+}
 
diff --git a/thunar-vfs/thunar-vfs-info.h b/thunar-vfs/thunar-vfs-info.h
index 6d1ab3eff3d4d22092b54c778f31fedc48197f8d..38e59e75515f32a13df3dacaefffe1fe61ded546 100644
--- a/thunar-vfs/thunar-vfs-info.h
+++ b/thunar-vfs/thunar-vfs-info.h
@@ -26,6 +26,7 @@
 
 #include <exo/exo.h>
 
+#include <thunar-vfs/thunar-vfs-mime.h>
 #include <thunar-vfs/thunar-vfs-uri.h>
 
 G_BEGIN_DECLS;
@@ -187,52 +188,26 @@ typedef struct
   /* device id */
   ThunarVfsFileDevice device;
 
+  /* file's mime type */
+  ThunarVfsMimeInfo *mime_info;
+
   /* file's URI */
   ThunarVfsURI *uri;
-} ThunarVfsInfo;
 
-#define thunar_vfs_info_init(info)                                          \
-G_STMT_START {                                                              \
-  (info)->type = THUNAR_VFS_FILE_TYPE_UNKNOWN;                              \
-  (info)->ctime = (ThunarVfsFileTime) -1;                                   \
-  (info)->uri = NULL;                                                       \
-} G_STMT_END
-
-#define thunar_vfs_info_reset(info)                                         \
-G_STMT_START {                                                              \
-  if (G_LIKELY ((info)->uri != NULL))                                       \
-    {                                                                       \
-      thunar_vfs_uri_unref ((info)->uri);                                   \
-      (info)->uri = NULL;                                                   \
-    }                                                                       \
-  (info)->type = THUNAR_VFS_FILE_TYPE_UNKNOWN;                              \
-  (info)->ctime = (ThunarVfsFileTime) -1;                                   \
-} G_STMT_END
+  /*< private >*/
+  gint ref_count;
+} ThunarVfsInfo;
 
-/**
- * ThunarVfsInfoResult:
- * @THUNAR_VFS_INFO_NOCHANGE: file wasn't altered since last check.
- * @THUNAR_VFS_INFO_CHANGED : file was changed since last update.
- * @THUNAR_VFS_INFO_ERROR   : an error occured on checking the file.
- *
- * Possible return values for the #thunar_vfs_info_update() function,
- * which determine the result of the update.
- **/
-typedef enum
-{
-  THUNAR_VFS_INFO_RESULT_NOCHANGE,
-  THUNAR_VFS_INFO_RESULT_CHANGED,
-  THUNAR_VFS_INFO_RESULT_ERROR,
-} ThunarVfsInfoResult;
+ThunarVfsInfo *thunar_vfs_info_new_for_uri (ThunarVfsURI        *uri,
+                                            GError             **error) G_GNUC_MALLOC;
 
-gboolean            thunar_vfs_info_query            (ThunarVfsInfo       *info,
-                                                      ThunarVfsURI        *uri,
-                                                      GError             **error);
+ThunarVfsInfo *thunar_vfs_info_ref         (ThunarVfsInfo       *info);
+void           thunar_vfs_info_unref       (ThunarVfsInfo       *info);
 
-ThunarVfsInfoResult thunar_vfs_info_update           (ThunarVfsInfo       *info,
-                                                      GError             **error);
+gboolean       thunar_vfs_info_matches     (const ThunarVfsInfo *a,
+                                            const ThunarVfsInfo *b);
 
-ExoMimeInfo        *thunar_vfs_info_get_mime_info    (ThunarVfsInfo        *info);
+GSList        *thunar_vfs_info_list_free   (GSList              *info_list);
 
 G_END_DECLS;
 
diff --git a/thunar-vfs/thunar-vfs-job-listdir.c b/thunar-vfs/thunar-vfs-job-listdir.c
new file mode 100644
index 0000000000000000000000000000000000000000..d9816f401ed57bc0003bf6baebd8446cbc7fa5aa
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-job-listdir.c
@@ -0,0 +1,284 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; 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_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <thunar-vfs/thunar-vfs-job-listdir.h>
+
+
+
+typedef struct _ThunarVfsJobListdir ThunarVfsJobListdir;
+
+
+
+#define THUNAR_VFS_TYPE_JOB_LISTDIR     (thunar_vfs_job_listdir_get_type ())
+#define THUNAR_VFS_JOB_LISTDIR(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_JOB_LISTDIR, ThunarVfsJobListdir))
+#define THUNAR_VFS_IS_JOB_LISTDIR(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_JOB_LISTDIR))
+
+
+
+static GType thunar_vfs_job_listdir_get_type      (void) G_GNUC_CONST;
+static void  thunar_vfs_job_listdir_register_type (GType             *type);
+static void  thunar_vfs_job_listdir_class_init    (ThunarVfsJobClass *klass);
+static void  thunar_vfs_job_listdir_callback      (ThunarVfsJob      *job);
+static void  thunar_vfs_job_listdir_execute       (ThunarVfsJob      *job);
+static void  thunar_vfs_job_listdir_finalize      (ThunarVfsJob      *job);
+
+
+struct _ThunarVfsJobListdir
+{
+  ThunarVfsJob __parent__;
+
+  ThunarVfsURI *uri;
+  GSList       *infos;
+
+  ThunarVfsJobListdirCallback callback;
+  gpointer                    user_data;
+};
+
+
+
+static GType
+thunar_vfs_job_listdir_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+  static GOnce once = G_ONCE_INIT;
+
+  /* thread-safe type registration */
+  g_once (&once, (GThreadFunc) thunar_vfs_job_listdir_register_type, &type);
+
+  return type;
+}
+
+
+
+static void
+thunar_vfs_job_listdir_register_type (GType *type)
+{
+  static const GTypeInfo info =
+  {
+    sizeof (ThunarVfsJobClass),
+    NULL,
+    NULL,
+    (GClassInitFunc) thunar_vfs_job_listdir_class_init,
+    NULL,
+    NULL,
+    sizeof (ThunarVfsJobListdir),
+    0,
+    NULL,
+    NULL,
+  };
+
+  *type = g_type_register_static (THUNAR_VFS_TYPE_JOB,
+                                  "ThunarVfsJobListdir",
+                                  &info, 0);
+}
+
+
+
+static void
+thunar_vfs_job_listdir_class_init (ThunarVfsJobClass *klass)
+{
+  klass->callback = thunar_vfs_job_listdir_callback;
+  klass->execute = thunar_vfs_job_listdir_execute;
+  klass->finalize = thunar_vfs_job_listdir_finalize;
+}
+
+
+
+static void
+thunar_vfs_job_listdir_callback (ThunarVfsJob *job)
+{
+  (*THUNAR_VFS_JOB_LISTDIR (job)->callback) (job, THUNAR_VFS_JOB_LISTDIR (job)->infos,
+                                             THUNAR_VFS_JOB_LISTDIR (job)->user_data);
+}
+
+
+
+static void
+thunar_vfs_job_listdir_execute (ThunarVfsJob *job)
+{
+  ThunarVfsJobListdir *job_listdir = THUNAR_VFS_JOB_LISTDIR (job);
+  ThunarVfsInfo       *info;
+  struct dirent        d_buffer;
+  struct dirent       *d;
+  GStringChunk        *names_chunk;
+  ThunarVfsURI        *file_uri;
+  GSList              *names;
+  GSList              *lp;
+  time_t               last_check_time;
+  time_t               current_time;
+  DIR                 *dp;
+
+  dp = opendir (thunar_vfs_uri_get_path (job_listdir->uri));
+  if (G_UNLIKELY (dp == NULL))
+    {
+      thunar_vfs_job_set_error (job, G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
+    }
+  else
+    {
+      names_chunk = g_string_chunk_new (4 * 1024);
+      names = NULL;
+
+      /* We read the file names first here to
+       * (hopefully) avoid a bit unnecessary
+       * disk seeking.
+       */
+      for (;;)
+        {
+#ifdef HAVE_READDIR_R
+          if (G_UNLIKELY (readdir_r (dp, &d_buffer, &d) < 0))
+            {
+              thunar_vfs_job_set_error (job, G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
+              break;
+            }
+#else
+          G_LOCK (readdir);
+          d = readdir (dp);
+          if (G_LIKELY (d != NULL))
+            {
+              memcpy (&d_buffer, d, sizeof (*d));
+              d = &d_buffer;
+            }
+          G_UNLOCK (readdir);
+#endif
+
+          /* check for end of directory */
+          if (G_UNLIKELY (d == NULL))
+              break;
+
+          /* ignore "." and ".." entries */
+          if (G_UNLIKELY (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))))
+            continue;
+
+          names = g_slist_insert_sorted (names, g_string_chunk_insert (names_chunk, d->d_name), (GCompareFunc) strcmp);
+        }
+
+      closedir (dp);
+
+      last_check_time = time (NULL);
+
+      /* Next we query the info for each of the file names
+       * queried in the loop above.
+       */
+      for (lp = names; lp != NULL; lp = lp->next)
+        {
+          /* check for cancellation/failure condition */
+          if (thunar_vfs_job_cancelled (job) || thunar_vfs_job_failed (job))
+            break;
+
+          file_uri = thunar_vfs_uri_relative (job_listdir->uri, lp->data);
+          info = thunar_vfs_info_new_for_uri (file_uri, NULL);
+          if (G_LIKELY (info != NULL))
+            job_listdir->infos = g_slist_prepend (job_listdir->infos, info);
+          thunar_vfs_uri_unref (file_uri);
+
+          current_time = time (NULL);
+          if (current_time - last_check_time > 2)
+            {
+              last_check_time = current_time;
+              thunar_vfs_job_callback (job);
+              thunar_vfs_info_list_free (job_listdir->infos);
+              job_listdir->infos = NULL;
+            }
+        }
+
+      /* free the names */
+      g_string_chunk_free (names_chunk);
+      g_slist_free (names);
+    }
+
+  /* we did it */
+  thunar_vfs_job_finish (job);
+  thunar_vfs_job_callback (job);
+}
+
+
+
+static void
+thunar_vfs_job_listdir_finalize (ThunarVfsJob *job)
+{
+  ThunarVfsJobListdir *job_listdir = THUNAR_VFS_JOB_LISTDIR (job);
+
+  /* free the folder uri */
+  if (G_LIKELY (job_listdir->uri != NULL))
+    thunar_vfs_uri_unref (job_listdir->uri);
+
+  /* free the leftover files */
+  thunar_vfs_info_list_free (job_listdir->infos);
+}
+
+
+
+/**
+ * thunar_vfs_job_listdir:
+ * @folder_uri : the #ThunarVfsURI of the directory whose content to query.
+ * @callback   : the callback function to handle the retrieved infos.
+ * @user_data  : additional data to pass to @callback.
+ *
+ * FIXME: DOC!
+ *
+ * The caller is responsible for freeing the returned #ThunarVfsJob
+ * using the #thunar_vfs_job_unref() function.
+ *
+ * Return value: the newly allocated #ThunarVfsJob, which performs
+ *               the directory listing.
+ **/
+ThunarVfsJob*
+thunar_vfs_job_listdir (ThunarVfsURI               *folder_uri,
+                        ThunarVfsJobListdirCallback callback,
+                        gpointer                    user_data)
+{
+  ThunarVfsJobListdir *job_listdir;
+
+  g_return_val_if_fail (THUNAR_VFS_IS_URI (folder_uri), NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
+
+  job_listdir = (ThunarVfsJobListdir *) g_type_create_instance (THUNAR_VFS_TYPE_JOB_LISTDIR);
+  job_listdir->uri = thunar_vfs_uri_ref (folder_uri);
+  job_listdir->callback = callback;
+  job_listdir->user_data = user_data;
+
+  return thunar_vfs_job_schedule (THUNAR_VFS_JOB (job_listdir));
+}
+
+
diff --git a/thunar-vfs/thunar-vfs-job-listdir.h b/thunar-vfs/thunar-vfs-job-listdir.h
new file mode 100644
index 0000000000000000000000000000000000000000..faef070b63cd428824fa34cae503ad5beae5a847
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-job-listdir.h
@@ -0,0 +1,44 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __THUNAR_VFS_JOB_LISTDIR_H__
+#define __THUNAR_VFS_JOB_LISTDIR_H__
+
+#include <thunar-vfs/thunar-vfs-job.h>
+
+G_BEGIN_DECLS;
+
+/**
+ * ThunarVfsJobListdirCallback:
+ * @job       :
+ * @infos     :
+ * @user_data :
+ **/
+typedef void (*ThunarVfsJobListdirCallback) (ThunarVfsJob *job,
+                                             GSList       *infos,
+                                             gpointer      user_data);
+
+ThunarVfsJob *thunar_vfs_job_listdir (ThunarVfsURI               *folder_uri,
+                                      ThunarVfsJobListdirCallback callback,
+                                      gpointer                    user_data);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_VFS_JOB_LISTDIR_H__ */
diff --git a/thunar-vfs/thunar-vfs-job.c b/thunar-vfs/thunar-vfs-job.c
new file mode 100644
index 0000000000000000000000000000000000000000..b702ab5891786bd0ea113e86f89eb2017e595ff8
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-job.c
@@ -0,0 +1,654 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; 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_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <thunar-vfs/thunar-vfs-job.h>
+
+
+
+static void     thunar_vfs_job_register_type (GType        *type);
+static void     thunar_vfs_job_init          (ThunarVfsJob *job);
+static void     thunar_vfs_job_execute       (gpointer      data,
+                                              gpointer      user_data);
+static gboolean thunar_vfs_job_idle          (gpointer      user_data);
+
+
+
+static GThreadPool *job_pool = NULL;
+
+
+
+GType
+thunar_vfs_job_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+  static GOnce once = G_ONCE_INIT;
+
+  /* thread-safe type registration */
+  g_once (&once, (GThreadFunc) thunar_vfs_job_register_type, &type);
+
+  return type;
+}
+
+
+
+static void
+thunar_vfs_job_register_type (GType *type)
+{
+  static const GTypeFundamentalInfo finfo =
+  {
+    G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE
+  };
+
+  static const GTypeInfo info =
+  {
+    sizeof (ThunarVfsJobClass),
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    sizeof (ThunarVfsJob),
+    0,
+    (GInstanceInitFunc) thunar_vfs_job_init,
+    NULL,
+  };
+
+  *type = g_type_register_fundamental (g_type_fundamental_next (), "ThunarVfsJob", &info, &finfo, G_TYPE_FLAG_ABSTRACT);
+}
+
+
+
+static void
+thunar_vfs_job_init (ThunarVfsJob *job)
+{
+  job->ref_count = 1;
+  job->cond = g_cond_new ();
+  job->mutex = g_mutex_new ();
+}
+
+
+
+static void
+thunar_vfs_job_execute (gpointer data,
+                        gpointer user_data)
+{
+  ThunarVfsJob *job = THUNAR_VFS_JOB (data);
+
+  /* perform the real work */
+  (*THUNAR_VFS_JOB_GET_CLASS (job)->execute) (job);
+
+  /* cleanup */
+  thunar_vfs_job_unref (job);
+}
+
+
+
+static gboolean
+thunar_vfs_job_idle (gpointer user_data)
+{
+  ThunarVfsJob *job = THUNAR_VFS_JOB (user_data);
+
+  g_return_val_if_fail (THUNAR_VFS_IS_JOB (job), FALSE);
+
+  g_mutex_lock (job->mutex);
+
+  /* invoke the callback if the job wasn't cancelled */
+  if (G_LIKELY (!thunar_vfs_job_cancelled (job)))
+    THUNAR_VFS_JOB_GET_CLASS (job)->callback (job);
+
+  g_cond_signal (job->cond);
+  g_mutex_unlock (job->mutex);
+
+  /* remove the idle source */
+  return FALSE;
+}
+
+
+
+/**
+ * thunar_vfs_job_ref:
+ * @job : a #ThunarVfsJob.
+ *
+ * Increments the reference count on @job by
+ * 1 and returns a pointer to @job.
+ *
+ * Return value: a pointer to @job.
+ **/
+ThunarVfsJob*
+thunar_vfs_job_ref (ThunarVfsJob *job)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_JOB (job), NULL);
+  g_return_val_if_fail (job->ref_count > 0, NULL);
+
+  g_atomic_int_inc (&job->ref_count);
+
+  return job;
+}
+
+
+
+/**
+ * thunar_vfs_job_unref:
+ * @job : a #ThunarVfsJob.
+ *
+ * Decrements the reference count on @job by
+ * 1. If the reference count drops to zero,
+ * the resources allocated to @job will be
+ * freed.
+ **/
+void
+thunar_vfs_job_unref (ThunarVfsJob *job)
+{
+  g_return_if_fail (THUNAR_VFS_IS_JOB (job));
+  g_return_if_fail (job->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test (&job->ref_count))
+    {
+      /* call the finalize method (if any) */
+      if (THUNAR_VFS_JOB_GET_CLASS (job)->finalize != NULL)
+        (*THUNAR_VFS_JOB_GET_CLASS (job)->finalize) (job);
+
+      if (G_UNLIKELY (job->error != NULL))
+        g_error_free (job->error);
+
+      g_mutex_free (job->mutex);
+      g_cond_free (job->cond);
+
+      g_type_free_instance ((GTypeInstance *) job);
+    }
+}
+
+
+
+/**
+ * thunar_vfs_job_cancel:
+ * @job : a #ThunarVfsJob.
+ *
+ * Attempts to cancel the operation currently
+ * performed by @job. No single callback will
+ * be invoked for a cancelled job, you need
+ * to take that into account.
+ **/
+void
+thunar_vfs_job_cancel (ThunarVfsJob *job)
+{
+  g_return_if_fail (THUNAR_VFS_IS_JOB (job));
+  g_return_if_fail (job->ref_count > 0);
+
+  job->cancelled = TRUE;
+}
+
+
+
+/**
+ * thunar_vfs_job_cancelled:
+ * @job : a #ThunarVfsJob.
+ *
+ * Checks whether @job was previously cancelled
+ * by a call to #thunar_vfs_job_cancel().
+ *
+ * Return value: %TRUE if @job is cancelled.
+ **/
+gboolean
+thunar_vfs_job_cancelled (const ThunarVfsJob *job)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_JOB (job), FALSE);
+  g_return_val_if_fail (job->ref_count > 0, FALSE);
+
+  return job->cancelled;
+}
+
+
+
+/**
+ * thunar_vfs_job_failed:
+ * @job : a #ThunarVfsJob.
+ *
+ * Checks whether the execution of @job failed with
+ * an error. You can use #thunar_vfs_job_get_error()
+ * to query the error cause.
+ *
+ * Return value: %TRUE if @job failed.
+ **/
+gboolean
+thunar_vfs_job_failed (const ThunarVfsJob *job)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_JOB (job), FALSE);
+  g_return_val_if_fail (job->ref_count > 0, FALSE);
+
+  return (job->error != NULL);
+}
+
+
+
+/**
+ * thunar_vfs_job_finished:
+ * @job : a #ThunarVfsJob.
+ *
+ * Checks whether the execution of @job is finished,
+ * either successfull or not. You can use
+ * #thunar_vfs_job_failed() to determine whether the
+ * execution terminated successfully.
+ *
+ * Return value: %TRUE if @job is done.
+ **/
+gboolean
+thunar_vfs_job_finished (const ThunarVfsJob *job)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_JOB (job), FALSE);
+  g_return_val_if_fail (job->ref_count > 0, FALSE);
+
+  return job->finished;
+}
+
+
+
+/**
+ * thunar_vfs_job_get_error:
+ * @job : a #ThunarVfsJob.
+ *
+ * Returns the #GError that describes the exact cause
+ * of a failure during the execution of @job, or %NULL
+ * if @job is not finished or did not fail. Use
+ * #thunar_vfs_job_failed() to see whether the @job
+ * failed.
+ *
+ * The returned #GError instance is owned by @job, so
+ * the caller must not free it.
+ *
+ * Return value: the #GError describing the failure
+ *               cause for @job, or %NULL.
+ **/
+GError*
+thunar_vfs_job_get_error (const ThunarVfsJob *job)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_JOB (job), NULL);
+  g_return_val_if_fail (job->ref_count > 0, NULL);
+
+  return job->error;
+}
+
+
+
+/**
+ * thunar_vfs_job_finish:
+ * @job : a #ThunarVfsJob.
+ *
+ * Marks the @job as finished (if not already finished).
+ *
+ * This method is part of the module API and must not
+ * be called by application code.
+ **/
+void
+thunar_vfs_job_finish (ThunarVfsJob *job)
+{
+  g_return_if_fail (THUNAR_VFS_IS_JOB (job));
+  g_return_if_fail (job->ref_count > 0);
+
+  job->finished = TRUE;
+}
+
+
+
+/**
+ * thunar_vfs_job_schedule:
+ * @job : a #ThunarVfsJob.
+ *
+ * This functions schedules @job to be run as soon
+ * as possible, in a separate thread.
+ *
+ * Return value: a pointer to @job.
+ **/
+ThunarVfsJob*
+thunar_vfs_job_schedule (ThunarVfsJob *job)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_JOB (job), NULL);
+  g_return_val_if_fail (job->ref_count > 0, NULL);
+  g_return_val_if_fail (job_pool != NULL, NULL);
+
+  /* schedule the job on the thread pool */
+  g_thread_pool_push (job_pool, thunar_vfs_job_ref (job), NULL);
+
+  return job;
+}
+
+
+
+/**
+ * thunar_vfs_job_callback:
+ * @job : a #ThunarVfsJob.
+ **/
+void
+thunar_vfs_job_callback (ThunarVfsJob *job)
+{
+  g_return_if_fail (THUNAR_VFS_IS_JOB (job));
+  g_return_if_fail (job->ref_count > 0);
+
+  if (G_LIKELY (!thunar_vfs_job_cancelled (job)))
+    {
+      g_mutex_lock (job->mutex);
+      g_idle_add_full (G_PRIORITY_HIGH, thunar_vfs_job_idle, job, NULL);
+      g_cond_wait (job->cond, job->mutex);
+      g_mutex_unlock (job->mutex);
+    }
+}
+
+
+
+/**
+ * thunar_vfs_job_set_error:
+ * @job     : a #ThunarVfsJob.
+ * @domain  : the #GError domain.
+ * @code    : the #GError code.
+ * @message : an error message.
+ *
+ * Marks @job as failed and finished, and sets the failure
+ * cause according to the given error parameters.
+ *
+ * This method is part of the module API and must not
+ * be called by application code.
+ **/
+void
+thunar_vfs_job_set_error (ThunarVfsJob *job,
+                          GQuark        domain,
+                          gint          code,
+                          const gchar  *message)
+{
+  g_return_if_fail (THUNAR_VFS_IS_JOB (job));
+  g_return_if_fail (job->ref_count > 0);
+  g_return_if_fail (message != NULL);
+
+  if (G_UNLIKELY (job->error != NULL))
+    g_error_free (job->error);
+
+  job->error = g_error_new_literal (domain, code, message);
+  job->finished = TRUE;
+}
+
+
+
+
+
+
+
+
+#if 0
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <thunar-vfs/thunar-vfs-job.h>
+
+
+
+typedef struct _ThunarVfsJobReadFolder ThunarVfsJobReadFolder;
+
+
+
+static void thunar_vfs_job_callback (ThunarVfsJob *job);
+
+
+
+struct _ThunarVfsJob
+{
+  volatile gint cancelled;
+
+  void (*callback_func) (ThunarVfsJob *job);
+  void (*execute_func) (ThunarVfsJob *job);
+  void (*free_func) (ThunarVfsJob *job);
+};
+
+struct _ThunarVfsJobReadFolder
+{
+  ThunarVfsJob __parent__;
+
+  ThunarVfsURI *uri;
+  GSList       *infos;
+  GError       *error;
+
+  ThunarVfsJobReadFolderCallback callback;
+  gpointer                       user_data;
+};
+
+
+
+static GThreadPool *job_pool = NULL;
+
+#ifndef HAVE_READDIR_R
+G_LOCK_DEFINE_STATIC (readdir);
+#endif
+
+
+
+static void
+thunar_vfs_job_execute (gpointer data,
+                        gpointer user_data)
+{
+  ThunarVfsJob *job = (ThunarVfsJob *) data;
+  (*job->execute_func) (job);
+  (*job->free_func) (job);
+}
+
+
+
+static void
+thunar_vfs_job_schedule (ThunarVfsJob *job)
+{
+  g_thread_pool_push (job_pool, job, NULL);
+}
+
+
+
+static void
+thunar_vfs_job_read_folder_free (ThunarVfsJob *job)
+{
+  ThunarVfsJobReadFolder *folder_job = (ThunarVfsJobReadFolder *) job;
+
+  if (G_UNLIKELY (folder_job->error != NULL))
+    g_error_free (folder_job->error);
+
+  thunar_vfs_info_list_free (folder_job->infos);
+  thunar_vfs_uri_unref (folder_job->uri);
+
+  g_free (folder_job);
+}
+
+
+
+static void
+thunar_vfs_job_read_folder_callback (ThunarVfsJob *job)
+{
+  ThunarVfsJobReadFolder *folder_job = (ThunarVfsJobReadFolder *) job;
+  (*folder_job->callback) (job, folder_job->error, folder_job->infos, folder_job->user_data);
+}
+
+
+
+static void
+thunar_vfs_job_read_folder_execute (ThunarVfsJob *job)
+{
+  ThunarVfsJobReadFolder *folder_job = (ThunarVfsJobReadFolder *) job;
+  ThunarVfsInfo          *info;
+  struct dirent           d_buffer;
+  struct dirent          *d;
+  GStringChunk           *names_chunk;
+  ThunarVfsURI           *file_uri;
+  const gchar            *folder_path;
+  GSList                 *names;
+  GSList                 *lp;
+  DIR                    *dp;
+
+  folder_path = thunar_vfs_uri_get_path (folder_job->uri);
+
+  dp = opendir (folder_path);
+  if (G_UNLIKELY (dp == NULL))
+    {
+      folder_job->error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
+    }
+  else
+    {
+      names_chunk = g_string_chunk_new (4 * 1024);
+      names = NULL;
+
+      /* read the directory content */
+      for (;;)
+        {
+#ifdef HAVE_READDIR_R
+          if (G_UNLIKELY (readdir_r (dp, &d_buffer, &d) < 0))
+            {
+              folder_job->error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
+              break;
+            }
+#else
+          G_LOCK (readdir);
+          d = readdir (dp);
+          if (G_LIKELY (d != NULL))
+            {
+              memcpy (&d_buffer, d, sizeof (*d));
+              d = &d_buffer;
+            }
+          G_UNLOCK (readdir);
+#endif
+
+          if (G_UNLIKELY (d == NULL))
+            {
+              /* end of directory reached */
+              break;
+            }
+
+          names = g_slist_prepend (names, g_string_chunk_insert (names_chunk, d->d_name));
+        }
+
+      closedir (dp);
+
+      /* check if the job was neither cancelled nor was there an error */
+      if (G_LIKELY (!job->cancelled && folder_job->error == NULL))
+        {
+          for (lp = names; lp != NULL; lp = lp->next)
+            {
+              file_uri = thunar_vfs_uri_relative (folder_job->uri, lp->data);
+              info = thunar_vfs_info_new_for_uri (file_uri, NULL);
+              if (G_LIKELY (info != NULL))
+                folder_job->infos = g_slist_prepend (folder_job->infos, info);
+              thunar_vfs_uri_unref (file_uri);
+            }
+        }
+
+      /* free the names */
+      g_string_chunk_free (names_chunk);
+      g_slist_free (names);
+    }
+
+  thunar_vfs_job_callback (job);
+}
+
+
+
+/**
+ * _thunar_vfs_job_init:
+ *
+ * Initializes the jobs module of the ThunarVFS
+ * library.
+ **/
+void
+_thunar_vfs_job_init (void)
+{
+  g_return_if_fail (job_pool == NULL);
+
+  job_pool = g_thread_pool_new (thunar_vfs_job_execute, NULL, 8, FALSE, NULL);
+}
+
+
+
+/**
+ **/
+void
+thunar_vfs_job_cancel (ThunarVfsJob *job)
+{
+  g_return_if_fail (job != NULL);
+  g_return_if_fail (!job->cancelled);
+
+  g_atomic_int_inc ((gint *) &job->cancelled);
+}
+
+
+
+/**
+ **/
+ThunarVfsJob*
+thunar_vfs_job_read_folder (ThunarVfsURI                  *folder_uri,
+                            ThunarVfsJobReadFolderCallback callback,
+                            gpointer                       user_data)
+{
+  ThunarVfsJobReadFolder *folder_job;
+
+  g_return_val_if_fail (THUNAR_VFS_IS_URI (folder_uri), NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
+
+  folder_job = g_new0 (ThunarVfsJobReadFolder, 1);
+  folder_job->uri = thunar_vfs_uri_ref (folder_uri);
+  folder_job->callback = callback;
+  folder_job->user_data = user_data;
+
+  THUNAR_VFS_JOB (folder_job)->callback_func = thunar_vfs_job_read_folder_callback;
+  THUNAR_VFS_JOB (folder_job)->execute_func = thunar_vfs_job_read_folder_execute;
+  THUNAR_VFS_JOB (folder_job)->free_func = thunar_vfs_job_read_folder_free;
+
+  thunar_vfs_job_schedule (THUNAR_VFS_JOB (folder_job));
+
+  return THUNAR_VFS_JOB (folder_job);
+}
+#endif
+
+
+
+/**
+ * _thunar_vfs_job_init:
+ *
+ * Initializes the jobs module of the ThunarVFS
+ * library.
+ **/
+void
+_thunar_vfs_job_init (void)
+{
+  g_return_if_fail (job_pool == NULL);
+
+  job_pool = g_thread_pool_new (thunar_vfs_job_execute, NULL, 8, FALSE, NULL);
+}
+
+
diff --git a/thunar-vfs/thunar-vfs-job.h b/thunar-vfs/thunar-vfs-job.h
new file mode 100644
index 0000000000000000000000000000000000000000..24269fecc55e63958fbc85ce5a5ee0d94ea50fc1
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-job.h
@@ -0,0 +1,82 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __THUNAR_VFS_JOB_H__
+#define __THUNAR_VFS_JOB_H__
+
+#include <thunar-vfs/thunar-vfs-info.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarVfsJobClass ThunarVfsJobClass;
+typedef struct _ThunarVfsJob      ThunarVfsJob;
+
+#define THUNAR_VFS_TYPE_JOB             (thunar_vfs_job_get_type ())
+#define THUNAR_VFS_JOB(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_JOB, ThunarVfsJob))
+#define THUNAR_VFS_JOB_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_VFS_TYPE_JOB, ThunarVfsJobClass))
+#define THUNAR_VFS_IS_JOB(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_JOB))
+#define THUNAR_VFS_IS_JOB_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_VFS_TYPE_JOB))
+#define THUNAR_VFS_JOB_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_VFS_TYPE_JOB, ThunarVfsJobClass))
+
+struct _ThunarVfsJobClass
+{
+  GTypeClass __parent__;
+
+  /* virtual methods */
+  void (*callback) (ThunarVfsJob *job);
+  void (*execute)  (ThunarVfsJob *job);
+  void (*finalize) (ThunarVfsJob *job);
+};
+
+struct _ThunarVfsJob
+{
+  GTypeInstance __parent__;
+
+  gint     ref_count;
+  GCond   *cond;
+  GMutex  *mutex;
+  GError  *error;
+  gboolean cancelled;
+  gboolean finished;
+};
+
+GType         thunar_vfs_job_get_type  (void) G_GNUC_CONST;
+
+/* public API */
+ThunarVfsJob *thunar_vfs_job_ref       (ThunarVfsJob       *job);
+void          thunar_vfs_job_unref     (ThunarVfsJob       *job);
+void          thunar_vfs_job_cancel    (ThunarVfsJob       *job);
+gboolean      thunar_vfs_job_cancelled (const ThunarVfsJob *job);
+gboolean      thunar_vfs_job_failed    (const ThunarVfsJob *job);
+gboolean      thunar_vfs_job_finished  (const ThunarVfsJob *job);
+GError       *thunar_vfs_job_get_error (const ThunarVfsJob *job);
+
+/* module API */
+void          thunar_vfs_job_finish    (ThunarVfsJob       *job);
+ThunarVfsJob *thunar_vfs_job_schedule  (ThunarVfsJob       *job);
+void          thunar_vfs_job_callback  (ThunarVfsJob       *job);
+void          thunar_vfs_job_set_error (ThunarVfsJob       *job,
+                                        GQuark              domain,
+                                        gint                code,
+                                        const gchar        *message);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_VFS_JOB_H__ */
diff --git a/thunar-vfs/thunar-vfs-mime-parser.c b/thunar-vfs/thunar-vfs-mime-parser.c
new file mode 100644
index 0000000000000000000000000000000000000000..a28b7df0073e132340ec8f66eb0500bf567e9900
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-mime-parser.c
@@ -0,0 +1,257 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; 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_LOCALE_H
+#include <locale.h>
+#endif
+
+#include <thunar-vfs/thunar-vfs-mime-parser.h>
+
+
+
+typedef enum
+{
+  PARSER_STATE_START,
+  PARSER_STATE_MIMETYPE,
+  PARSER_STATE_COMMENT,
+  PARSER_STATE_UNKNOWN,
+} ParserState;
+ 
+typedef XFCE_GENERIC_STACK(ParserState) ParserStateStack;
+ 
+typedef struct
+{
+  ParserStateStack *stack;
+  guint             comment_match;
+  gboolean          comment_use;
+  GString          *comment;
+  const gchar      *locale;
+} Parser;
+ 
+ 
+ 
+static void         start_element_handler     (GMarkupParseContext  *context,
+                                               const gchar          *element_name,
+                                               const gchar         **attribute_names,
+                                               const gchar         **attribute_values,
+                                               gpointer              user_data,
+                                               GError              **error);
+static void         end_element_handler       (GMarkupParseContext  *context,
+                                               const gchar          *element_name,
+                                               gpointer              user_data,
+                                               GError              **error);
+static void         text_handler              (GMarkupParseContext  *context,
+                                               const gchar          *text,
+                                               gsize                 text_len,
+                                               gpointer              user_data,
+                                               GError             **error);
+ 
+ 
+ 
+static GMarkupParser markup_parser =
+{
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  NULL,
+  NULL,
+};
+ 
+ 
+ 
+static void
+start_element_handler (GMarkupParseContext  *context,
+                       const gchar          *element_name,
+                       const gchar         **attribute_names,
+                       const gchar         **attribute_values,
+                       gpointer              user_data,
+                       GError              **error)
+{
+  Parser *parser = (Parser *) user_data;
+  guint   match;
+  guint   n;
+ 
+  switch (xfce_stack_top (parser->stack))
+    {
+    case PARSER_STATE_START:
+      if (exo_str_is_equal (element_name, "mime-type"))
+        {
+          xfce_stack_push (parser->stack, PARSER_STATE_MIMETYPE);
+        }
+      else
+        goto unknown_element;
+      break;
+ 
+    case PARSER_STATE_MIMETYPE:
+      if (exo_str_is_equal (element_name, "comment"))
+        {
+          for (n = 0; attribute_names[n] != NULL; ++n)
+            if (exo_str_is_equal (attribute_names[n], "xml:lang"))
+              break;
+ 
+          if (G_UNLIKELY (attribute_names[n] == NULL))
+            {
+              parser->comment_use = (parser->comment_match <= XFCE_LOCALE_NO_MATCH);
+            }
+          else
+            {
+              match = xfce_locale_match (parser->locale, attribute_values[n]);
+              if (parser->comment_match < match)
+                {
+                  parser->comment_match = match;
+                  parser->comment_use = TRUE;
+                }
+              else
+                {
+                  parser->comment_use = FALSE;
+                }
+            }
+ 
+          if (parser->comment_use)
+            g_string_truncate (parser->comment, 0);
+ 
+          xfce_stack_push (parser->stack, PARSER_STATE_COMMENT);
+        }
+      else
+        xfce_stack_push (parser->stack, PARSER_STATE_UNKNOWN);
+      break;
+ 
+    default:
+      xfce_stack_push (parser->stack, PARSER_STATE_UNKNOWN);
+      break;
+    }
+ 
+  return;
+ 
+unknown_element:
+  g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+               "Unknown element <%s>", element_name);
+  return;
+}
+ 
+ 
+ 
+static void
+end_element_handler (GMarkupParseContext  *context,
+                     const gchar          *element_name,
+                     gpointer              user_data,
+                     GError              **error)
+{
+  Parser *parser = (Parser *) user_data;
+ 
+  switch (xfce_stack_top (parser->stack))
+    {
+    case PARSER_STATE_START:
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                   "End element handler called while in root context");
+      return;
+ 
+    case PARSER_STATE_MIMETYPE:
+      if (!exo_str_is_equal (element_name, "mime-type"))
+        goto unknown_element;
+      break;
+ 
+    case PARSER_STATE_COMMENT:
+      if (!exo_str_is_equal (element_name, "comment"))
+        goto unknown_element;
+      break;
+ 
+    default:
+      break;
+    }
+ 
+  xfce_stack_pop (parser->stack);
+  return;
+ 
+unknown_element:
+  g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+               "Unknown closing element <%s>", element_name);
+  return;
+}
+ 
+ 
+ 
+static void
+text_handler (GMarkupParseContext  *context,
+              const gchar          *text,
+              gsize                 text_len,
+              gpointer              user_data,
+              GError             **error)
+{
+  Parser *parser = (Parser *) user_data;
+ 
+  switch (xfce_stack_top (parser->stack))
+    {
+    case PARSER_STATE_COMMENT:
+      if (parser->comment_use)
+        g_string_append_len (parser->comment, text, text_len);
+      break;
+ 
+    default:
+      break;
+    }
+}
+ 
+ 
+ 
+gchar*
+_thunar_vfs_mime_parser_load_comment_from_file (const gchar *filename,
+                                                GError     **error)
+{
+  GMarkupParseContext *context;
+  Parser               parser;
+  gchar               *content;
+  gsize                content_len;
+  gboolean             comment_free = TRUE;
+  gchar               *comment = NULL;
+ 
+  if (!g_file_get_contents (filename, &content, &content_len, error))
+    return NULL;
+ 
+  parser.comment_match = XFCE_LOCALE_NO_MATCH;
+  parser.comment = g_string_new ("");
+  parser.locale = setlocale (LC_MESSAGES, NULL);
+ 
+  parser.stack = xfce_stack_new (ParserStateStack);
+  xfce_stack_push (parser.stack, PARSER_STATE_START);
+ 
+  context = g_markup_parse_context_new (&markup_parser, 0, &parser, NULL);
+ 
+  if (!g_markup_parse_context_parse (context, content, content_len, error))
+    goto done;
+ 
+  if (!g_markup_parse_context_end_parse (context, error))
+    goto done;
+ 
+  comment = parser.comment->str;
+  comment_free = FALSE;
+ 
+done:
+  g_markup_parse_context_free (context);
+  g_string_free (parser.comment, comment_free);
+  xfce_stack_free (parser.stack);
+  g_free (content);
+ 
+  return comment;
+}
diff --git a/thunar-vfs/thunar-vfs-mime-parser.h b/thunar-vfs/thunar-vfs-mime-parser.h
new file mode 100644
index 0000000000000000000000000000000000000000..ae0a9d68a1c136c15a6e9599a22941c23dee3624
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-mime-parser.h
@@ -0,0 +1,33 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __THUNAR_VFS_MIME_PARSER_H__
+#define __THUNAR_VFS_MIME_PARSER_H__
+
+#include <exo/exo.h>
+
+G_BEGIN_DECLS;
+
+gchar *_thunar_vfs_mime_parser_load_comment_from_file (const gchar *filename,
+                                                       GError     **error);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_VFS_MIME_PARSER_H__ */
diff --git a/thunar-vfs/thunar-vfs-mime.c b/thunar-vfs/thunar-vfs-mime.c
new file mode 100644
index 0000000000000000000000000000000000000000..f148c7ea1dfdbffced4c777f7d16390e46910242
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-mime.c
@@ -0,0 +1,559 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; 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_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <thunar-vfs/thunar-vfs-mime.h>
+#include <thunar-vfs/thunar-vfs-mime-parser.h>
+
+#include <xdgmime/xdgmime.h>
+
+
+
+static const struct
+{
+  const gchar *const type;
+  const gchar *const icon;
+} GNOME_ICONNAMES[] =
+{
+  { "inode/directory", "gnome-fs-directory" },
+  { "inode/blockdevice", "gnome-fs-blockdev" },
+  { "inode/chardevice", "gnome-fs-chardev" },
+  { "inode/fifo", "gnome-fs-fifo" },
+  { "inode/socket", "gnome-fs-socket" },
+};
+
+
+
+static void               thunar_vfs_mime_info_register_type      (GType             *type);
+static ThunarVfsMimeInfo *thunar_vfs_mime_info_get_unlocked       (const gchar       *name);
+static void               thunar_vfs_mime_info_icon_theme_changed (GtkIconTheme      *icon_theme,
+                                                                   ThunarVfsMimeInfo *info);
+
+
+
+struct _ThunarVfsMimeInfoClass
+{
+  GTypeClass __parent__;
+};
+
+struct _ThunarVfsMimeInfo
+{
+  GTypeInstance __parent__;
+
+  gint          ref_count;
+
+  gchar        *comment;
+  gchar        *name;
+
+  const gchar  *media;
+  const gchar  *subtype;
+
+  gchar        *icon_name;
+  gboolean      icon_name_static : 1;
+  GtkIconTheme *icon_theme;
+};
+
+
+
+static GHashTable *mime_infos = NULL;
+G_LOCK_DEFINE_STATIC (mime_infos);
+
+
+
+GType
+thunar_vfs_mime_info_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+  static GOnce once = G_ONCE_INIT;
+
+  /* thread-safe type registration */
+  g_once (&once, (GThreadFunc) thunar_vfs_mime_info_register_type, &type);
+
+  return type;
+}
+
+
+
+static void
+thunar_vfs_mime_info_register_type (GType *type)
+{
+  static const GTypeFundamentalInfo finfo =
+  {
+    G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE
+  };
+
+  static const GTypeInfo info =
+  {
+    sizeof (ThunarVfsMimeInfoClass),
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    sizeof (ThunarVfsMimeInfo),
+    128u,
+    NULL,
+    NULL,
+  };
+
+  *type = g_type_register_fundamental (g_type_fundamental_next (), "ThunarVfsMimeInfo", &info, &finfo, 0);
+}
+
+
+
+static ThunarVfsMimeInfo*
+thunar_vfs_mime_info_get_unlocked (const gchar *name)
+{
+  ThunarVfsMimeInfo *info;
+  const gchar       *s;
+  const gchar       *t;
+  const gchar       *u;
+  gchar             *v;
+
+  /* check if we have a cached version of the mime type */
+  info = g_hash_table_lookup (mime_infos, name);
+  if (info == NULL)
+    {
+      for (s = NULL, t = name; *t != '\0'; ++t)
+        if (G_UNLIKELY (*t == '/'))
+          s = t;
+
+      /* we could do better here... */
+      if (G_UNLIKELY (s == NULL))
+        g_error ("Invalid MIME type '%s'", name);
+
+      /* allocate the MIME info instance */
+      info = (ThunarVfsMimeInfo *) g_type_create_instance (THUNAR_VFS_TYPE_MIME_INFO);
+      info->ref_count = 2;
+
+      /* allocate memory to store both the full name,
+       * as well as the media type alone.
+       */
+      info->name = g_new (gchar, (t - name) + (s - name) + 2);
+
+      /* copy full name (including the terminator) */
+      for (u = name, v = info->name; u <= t; ++u, ++v)
+        *v = *u;
+
+      /* set the subtype portion */
+      info->subtype = info->name + (s - name) + 1;
+
+      /* copy the media portion */
+      info->media = v;
+      for (u = name; u < s; ++u, ++v)
+        *v = *u;
+
+      /* terminate the media portion */
+      *v = '\0';
+
+      /* insert the mime type into the cache */
+      g_hash_table_insert (mime_infos, info->name, info);
+
+      g_assert (THUNAR_VFS_IS_MIME_INFO (info));
+    }
+  else
+    {
+      /* take a reference for the caller */
+      thunar_vfs_mime_info_ref (info);
+    }
+
+  return info;
+}
+
+
+
+static void
+thunar_vfs_mime_info_icon_theme_changed (GtkIconTheme      *icon_theme,
+                                         ThunarVfsMimeInfo *info)
+{
+  g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
+  g_return_if_fail (THUNAR_VFS_IS_MIME_INFO (info));
+  g_return_if_fail (info->icon_theme == icon_theme);
+
+  /* drop the cached icon name, so the next lookup
+   * call will perform a lookup again.
+   */
+  if (G_LIKELY (!info->icon_name_static))
+    {
+      g_free (info->icon_name);
+      info->icon_name = NULL;
+    }
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_ref:
+ * @info : a #ThunarVfsMimeInfo.
+ *
+ * Increments the reference count by 1 on @info, and
+ * returns a pointer to @info.
+ *
+ * Return value: a pointer to @info.
+ **/
+ThunarVfsMimeInfo*
+thunar_vfs_mime_info_ref (ThunarVfsMimeInfo *info)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_MIME_INFO (info), NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
+
+  g_atomic_int_inc (&info->ref_count);
+
+  return info;
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_unref:
+ * @info : a #ThunarVfsMimeInfo.
+ *
+ * Decrements the reference count on @info by
+ * 1. If the reference count drops to zero, the
+ * resources allocated to @info will be freed.
+ **/
+void
+thunar_vfs_mime_info_unref (ThunarVfsMimeInfo *info)
+{
+  g_return_if_fail (THUNAR_VFS_IS_MIME_INFO (info));
+  g_return_if_fail (info->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test (&info->ref_count))
+    {
+      /* free the comment */
+      if (info->comment != NULL && info->comment != info->name)
+        {
+#ifndef G_DISABLE_CHECKS
+          memset (info->comment, 0xaa, strlen (info->comment) + 1);
+#endif
+          g_free (info->comment);
+        }
+
+      /* free the name */
+#ifndef G_DISABLE_CHECKS
+      if (G_LIKELY (info->name != NULL))
+        memset (info->name, 0xaa, strlen (info->name) + 1);
+#endif
+      g_free (info->name);
+
+      /* disconnect from the icon theme (if any) */
+      if (G_LIKELY (info->icon_theme != NULL))
+        {
+          g_signal_handlers_disconnect_by_func (G_OBJECT (info->icon_theme), thunar_vfs_mime_info_icon_theme_changed, info);
+          g_object_unref (G_OBJECT (info->icon_theme));
+        }
+
+      /* free the icon name if it isn't one of the statics */
+      if (G_LIKELY (!info->icon_name_static && info->icon_name != NULL))
+        {
+#ifndef G_DISABLE_CHECKS
+          memset (info->icon_name, 0xaa, strlen (info->icon_name) + 1);
+#endif
+          g_free (info->icon_name);
+        }
+
+      g_type_free_instance ((GTypeInstance *) info);
+    }
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_get_comment:
+ * @info : a #ThunarVfsMimeInfo.
+ *
+ * Determines the description for the given @info.
+ *
+ * Note that this method MUST NOT be called from threads other than
+ * the main thread, because it's not thread-safe!
+ *
+ * Return value: the comment associated with the @info or the empty string
+ *               if no comment was provided.
+ */
+const gchar*
+thunar_vfs_mime_info_get_comment (ThunarVfsMimeInfo *info)
+{
+  gchar *path;
+  gchar *spec;
+
+  g_return_val_if_fail (THUNAR_VFS_IS_MIME_INFO (info), NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
+
+  if (G_UNLIKELY (info->comment == NULL))
+    {
+      spec = g_strdup_printf ("mime/%s.xml", info->name);
+      path = xfce_resource_lookup (XFCE_RESOURCE_DATA, spec);
+      g_free (spec);
+
+      if (G_LIKELY (path != NULL))
+        {
+          info->comment = _thunar_vfs_mime_parser_load_comment_from_file (path, NULL);
+          g_free (path);
+        }
+
+      if (G_UNLIKELY (info->comment == NULL))
+        info->comment = info->name;
+    }
+
+  return info->comment;
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_get_name:
+ * @info : a #ThunarVfsMimeInfo.
+ *
+ * Returns the full qualified name of the MIME type
+ * described by the @info object.
+ *
+ * Return value: the name of @info.
+ **/
+const gchar*
+thunar_vfs_mime_info_get_name (const ThunarVfsMimeInfo *info)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_MIME_INFO (info), NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
+
+  return info->name;
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_get_media:
+ * @info : a #ThunarVfsMimeInfo.
+ *
+ * Returns the media portion of the MIME type, e.g. if your
+ * #ThunarVfsMimeInfo instance refers to "text/plain", invoking
+ * this method will return "text".
+ *
+ * Return value: the media portion of the MIME type.
+ **/
+const gchar*
+thunar_vfs_mime_info_get_media (const ThunarVfsMimeInfo *info)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_MIME_INFO (info), NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
+
+  return info->media;
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_get_subtype:
+ * @info : a #ThunarVfsMimeInfo.
+ *
+ * Returns the subtype portion of the MIME type, e.g. if @info
+ * refers to "application/octect-stream", this method will
+ * return "octect-stream".
+ *
+ * Return value: the subtype portion of @info.
+ **/
+const gchar*
+thunar_vfs_mime_info_get_subtype (const ThunarVfsMimeInfo *info)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_MIME_INFO (info), NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
+
+  return info->subtype;
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_lookup_icon_name:
+ * @info       : a #ThunarVfsMimeInfo.
+ * @icon_theme : the #GtkIconTheme on which to perform the lookup.
+ *
+ * Tries to determine the name of a suitable icon for @info
+ * in @icon_theme. The returned icon name can then be used
+ * in calls to #gtk_icon_theme_lookup_icon() or
+ * #gtk_icon_theme_load_icon().
+ *
+ * Note that this method MUST NOT be called from threads other than
+ * the main thread, because it's not thread-safe!
+ *
+ * Return value: a suitable icon name for @info in @icon_theme.
+ **/
+const gchar*
+thunar_vfs_mime_info_lookup_icon_name (ThunarVfsMimeInfo *info,
+                                       GtkIconTheme      *icon_theme)
+{
+  gsize n;
+
+  g_return_val_if_fail (THUNAR_VFS_IS_MIME_INFO (info), NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
+  g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+
+  /* check if our cached name will suffice */
+  if (G_LIKELY (info->icon_theme == icon_theme && info->icon_name != NULL))
+    return info->icon_name;
+
+  /* if we have a new icon theme, connect to the new one */
+  if (G_UNLIKELY (info->icon_theme != icon_theme))
+    {
+      /* disconnect from the previous one */
+      if (G_LIKELY (info->icon_theme != NULL))
+        {
+          g_signal_handlers_disconnect_by_func (G_OBJECT (info->icon_theme), thunar_vfs_mime_info_icon_theme_changed, info);
+          g_object_unref (G_OBJECT (info->icon_theme));
+        }
+
+      /* connect to the new one */
+      info->icon_theme = icon_theme;
+      g_object_ref (G_OBJECT (icon_theme));
+      g_signal_connect (G_OBJECT (icon_theme), "changed", G_CALLBACK (thunar_vfs_mime_info_icon_theme_changed), info);
+    }
+
+  /* free the previously set icon name */
+  if (!info->icon_name_static)
+    g_free (info->icon_name);
+
+  /* start out with the full name (assuming a non-static icon_name) */
+  info->icon_name = g_strdup_printf ("gnome-mime-%s-%s", info->media, info->subtype);
+  info->icon_name_static = FALSE;
+  if (!gtk_icon_theme_has_icon (icon_theme, info->icon_name))
+    {
+      /* only the media portion */
+      info->icon_name[11 + ((info->subtype - 1) - info->name)] = '\0';
+      if (!gtk_icon_theme_has_icon (icon_theme, info->icon_name))
+        {
+          /* if we get here, we'll use a static icon name */
+          info->icon_name_static = TRUE;
+          g_free (info->icon_name);
+
+          /* GNOME uses non-standard names for special MIME types */
+          for (n = 0; n < G_N_ELEMENTS (GNOME_ICONNAMES); ++n)
+            if (exo_str_is_equal (info->name, GNOME_ICONNAMES[n].type))
+              if (gtk_icon_theme_has_icon (icon_theme, GNOME_ICONNAMES[n].icon))
+                {
+                  info->icon_name = (gchar *) GNOME_ICONNAMES[n].icon;
+                  break;
+                }
+
+          /* fallback is always application/octect-stream */
+          if (n == G_N_ELEMENTS (GNOME_ICONNAMES))
+            info->icon_name = (gchar *) "gnome-mime-application-octect-stream";
+        }
+    }
+
+  g_assert (info->icon_name != NULL);
+  g_assert (info->icon_name[0] != '\0');
+
+  return info->icon_name;
+}
+
+
+
+
+
+/**
+ * _thunar_vfs_mime_init:
+ *
+ * Initializes the MIME component of the ThunarVFS
+ * library.
+ **/
+void
+_thunar_vfs_mime_init (void)
+{
+  g_return_if_fail (mime_infos == NULL);
+
+  /* initialize the XDG mime library */
+  xdg_mime_init ();
+
+  /* allocate a string chunk for the mime types */
+  mime_infos = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) thunar_vfs_mime_info_unref);
+}
+
+
+
+/**
+ * thunar_vfs_mime_info_get:
+ * @mime_type : the string representation of a mime type.
+ *
+ * Returns the #ThunarVfsMimeInfo corresponding to 
+ * @mime_type.
+ *
+ * The caller is responsible to free the returned object
+ * using #thunar_vfs_mime_info_unref().
+ *
+ * Return value: the #ThunarVfsMimeInfo for @mime_type.
+ **/
+ThunarVfsMimeInfo*
+thunar_vfs_mime_info_get (const gchar *mime_type)
+{
+  ThunarVfsMimeInfo *info;
+
+  g_return_val_if_fail (mime_type != NULL, NULL);
+  g_return_val_if_fail (strchr (mime_type, '/') != NULL, NULL);
+  g_return_val_if_fail (strchr (mime_type, '/') == strrchr (mime_type, '/'), NULL);
+
+  G_LOCK (mime_infos);
+  info = thunar_vfs_mime_info_get_unlocked (mime_type);
+  G_UNLOCK (mime_infos);
+
+  return info;
+}
+
+
+ 
+/**
+ * thunar_vfs_mime_info_get_for_file:
+ * @path : the path to a local file.
+ *
+ * Determines the #ThunarVfsMimeInfo of the file referred to
+ * by @path.
+ *
+ * The caller is responsible to free the returned object
+ * using #thunar_vfs_mime_info_unref().
+ *
+ * Return value: the #ThunarVfsMimeInfo for @path.
+ **/
+ThunarVfsMimeInfo*
+thunar_vfs_mime_info_get_for_file (const gchar *path)
+{
+  ThunarVfsMimeInfo *info;
+  const gchar       *name;
+
+  g_return_val_if_fail (g_path_is_absolute (path), NULL);
+  g_return_val_if_fail (mime_infos != NULL, NULL);
+
+  G_LOCK (mime_infos);
+  name = xdg_mime_get_mime_type_for_file (path);
+  info = thunar_vfs_mime_info_get_unlocked (name);
+  G_UNLOCK (mime_infos);
+
+  return info;
+}
+
+
+
diff --git a/thunar-vfs/thunar-vfs-mime.h b/thunar-vfs/thunar-vfs-mime.h
new file mode 100644
index 0000000000000000000000000000000000000000..0386e4419df3b99eed972e5c0db8079359e0bc28
--- /dev/null
+++ b/thunar-vfs/thunar-vfs-mime.h
@@ -0,0 +1,57 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __THUNAR_VFS_MIME_H__
+#define __THUNAR_VFS_MIME_H__
+
+#include <exo/exo.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarVfsMimeInfoClass ThunarVfsMimeInfoClass;
+typedef struct _ThunarVfsMimeInfo      ThunarVfsMimeInfo;
+
+#define THUNAR_VFS_TYPE_MIME_INFO             (thunar_vfs_mime_info_get_type ())
+#define THUNAR_VFS_MIME_INFO(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_MIME_INFO, ThunarVfsMimeInfo))
+#define THUNAR_VFS_MIME_INFO_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_VFS_TYPE_MIME_INFO, ThunarVfsMimeInfoClass))
+#define THUNAR_VFS_IS_MIME_INFO(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_MIME_INFO))
+#define THUNAR_VFS_IS_MIME_INFO_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_VFS_TYPE_MIME_INFO))
+#define THUNAR_VFS_MIME_INFO_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_VFS_TYPE_MIME_INFO, ThunarVfsMimeInfoClass))
+
+GType              thunar_vfs_mime_info_get_type         (void) G_GNUC_CONST;
+
+ThunarVfsMimeInfo *thunar_vfs_mime_info_ref              (ThunarVfsMimeInfo       *info);
+void               thunar_vfs_mime_info_unref            (ThunarVfsMimeInfo       *info);
+
+ThunarVfsMimeInfo *thunar_vfs_mime_info_get              (const gchar             *mime_type);
+ThunarVfsMimeInfo *thunar_vfs_mime_info_get_for_file     (const gchar             *path);
+
+const gchar       *thunar_vfs_mime_info_get_comment      (ThunarVfsMimeInfo       *info) G_GNUC_PURE;
+const gchar       *thunar_vfs_mime_info_get_name         (const ThunarVfsMimeInfo *info) G_GNUC_PURE;
+
+const gchar       *thunar_vfs_mime_info_get_media        (const ThunarVfsMimeInfo *info) G_GNUC_PURE;
+const gchar       *thunar_vfs_mime_info_get_subtype      (const ThunarVfsMimeInfo *info) G_GNUC_PURE;
+
+const gchar       *thunar_vfs_mime_info_lookup_icon_name (ThunarVfsMimeInfo       *info,
+                                                          GtkIconTheme            *icon_theme);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_VFS_MIME_H__ */
diff --git a/thunar-vfs/thunar-vfs-monitor.c b/thunar-vfs/thunar-vfs-monitor.c
index 81c1c1b20a6638cc0254a5365d031be56c78227b..0fe3b2e6d1301c280540493829ce9478275870c8 100644
--- a/thunar-vfs/thunar-vfs-monitor.c
+++ b/thunar-vfs/thunar-vfs-monitor.c
@@ -208,6 +208,7 @@ thunar_vfs_monitor_event (GIOChannel  *source,
                           GIOCondition condition,
                           gpointer     user_data)
 {
+#if 0
 #ifdef HAVE_KQUEUE
   ThunarVfsInfoResult result;
   ThunarVfsMonitor   *monitor = THUNAR_VFS_MONITOR (user_data);
@@ -225,7 +226,7 @@ thunar_vfs_monitor_event (GIOChannel  *source,
         continue;
 
       /* force an update on the info */
-      result = thunar_vfs_info_update (watch->info, NULL);
+      result = 0; /*thunar_vfs_info_update (watch->info, NULL);*/
       switch (result)
         {
         case THUNAR_VFS_INFO_RESULT_ERROR:
@@ -246,6 +247,7 @@ thunar_vfs_monitor_event (GIOChannel  *source,
     }
 
   GDK_THREADS_LEAVE ();
+#endif
 #endif
 
   /* keep the kqueue monitor going */
@@ -257,6 +259,7 @@ thunar_vfs_monitor_event (GIOChannel  *source,
 static gboolean
 thunar_vfs_monitor_timer (gpointer user_data)
 {
+#if 0
   ThunarVfsInfoResult result;
   ThunarVfsMonitor   *monitor = THUNAR_VFS_MONITOR (user_data);
   ThunarVfsWatch     *watch;
@@ -350,7 +353,8 @@ thunar_vfs_monitor_timer (gpointer user_data)
     }
 
   GDK_THREADS_LEAVE ();
-
+#endif
+  
   return TRUE;
 }
 
diff --git a/thunar-vfs/thunar-vfs-uri.c b/thunar-vfs/thunar-vfs-uri.c
index 395f6d219a06468c4db5810f07792c21c22b67b6..cfdff6fc78c68ae09ac4844136b6ce0db662b1f2 100644
--- a/thunar-vfs/thunar-vfs-uri.c
+++ b/thunar-vfs/thunar-vfs-uri.c
@@ -56,7 +56,7 @@ struct _ThunarVfsURI
 {
   GTypeInstance __parent__;
 
-  guint              ref_count;
+  gint               ref_count;
   gchar             *host;
   gchar             *path;
   const gchar       *name;
@@ -409,7 +409,7 @@ thunar_vfs_uri_ref (ThunarVfsURI *uri)
   g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
   g_return_val_if_fail (uri->ref_count > 0, NULL);
 
-  ++uri->ref_count;
+  g_atomic_int_inc (&uri->ref_count);
 
   return uri;
 }
@@ -430,7 +430,7 @@ thunar_vfs_uri_unref (ThunarVfsURI *uri)
   g_return_if_fail (THUNAR_VFS_IS_URI (uri));
   g_return_if_fail (uri->ref_count > 0);
 
-  if (--uri->ref_count == 0)
+  if (g_atomic_int_dec_and_test (&uri->ref_count))
     {
 #ifndef G_DISABLE_CHECKS
       G_LOCK (debug_uris);
diff --git a/thunar-vfs/thunar-vfs-volume-sysv.c b/thunar-vfs/thunar-vfs-volume-sysv.c
index bbf2d00326f675cc1ac468c686ad0b942a5e87a8..d49cf39ebe51758f40aa3155fc4df80aaec814ce 100644
--- a/thunar-vfs/thunar-vfs-volume-sysv.c
+++ b/thunar-vfs/thunar-vfs-volume-sysv.c
@@ -29,7 +29,7 @@
 static void             thunar_vfs_volume_manager_sysv_class_init         (ThunarVfsVolumeManagerSysVClass *klass);
 static void             thunar_vfs_volume_manager_sysv_manager_init       (ThunarVfsVolumeManagerIface     *iface);
 static void             thunar_vfs_volume_manager_sysv_init               (ThunarVfsVolumeManagerSysV      *manager_sysv);
-static ThunarVfsVolume *thunar_vfs_volume_manager_sysv_get_volume_by_info (ThunarVfsVolumeManagerSysV      *manager_sysv,
+static ThunarVfsVolume *thunar_vfs_volume_manager_sysv_get_volume_by_info (ThunarVfsVolumeManager          *manager,
                                                                            const ThunarVfsInfo             *info);
 static GList           *thunar_vfs_volume_manager_sysv_get_volumes        (ThunarVfsVolumeManager          *manager);
 
@@ -78,8 +78,8 @@ thunar_vfs_volume_manager_sysv_init (ThunarVfsVolumeManagerSysV *manager_sysv)
 
 
 static ThunarVfsVolume*
-thunar_vfs_volume_manager_sysv_get_volume_by_info (ThunarVfsVolumeManagerSysV *manager_sysv,
-                                                   const ThunarVfsInfo        *info)
+thunar_vfs_volume_manager_sysv_get_volume_by_info (ThunarVfsVolumeManager *manager,
+                                                   const ThunarVfsInfo    *info)
 {
   return NULL;
 }
diff --git a/thunar-vfs/thunar-vfs.c b/thunar-vfs/thunar-vfs.c
new file mode 100644
index 0000000000000000000000000000000000000000..adaa43948ed969eaa0523f07a202a469312a06c1
--- /dev/null
+++ b/thunar-vfs/thunar-vfs.c
@@ -0,0 +1,51 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; 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 <thunar-vfs/thunar-vfs.h>
+
+
+
+/**
+ * thunar_vfs_init:
+ *
+ * Initializes the ThunarVFS library.
+ **/
+void
+thunar_vfs_init (void)
+{
+  extern void _thunar_vfs_job_init (void);
+  extern void _thunar_vfs_mime_init (void);
+
+  static gboolean initialized = FALSE;
+
+  if (!initialized)
+    {
+      _thunar_vfs_job_init ();
+      _thunar_vfs_mime_init ();
+
+      initialized = TRUE;
+    }
+}
+
+
diff --git a/thunar-vfs/thunar-vfs.h b/thunar-vfs/thunar-vfs.h
index 24c597b17581723e56534a29b1ed404e588e045c..52127a2c8312232cfd729a39b97fb01966f74dda 100644
--- a/thunar-vfs/thunar-vfs.h
+++ b/thunar-vfs/thunar-vfs.h
@@ -23,6 +23,9 @@
 
 #include <thunar-vfs/thunar-vfs-enum-types.h>
 #include <thunar-vfs/thunar-vfs-info.h>
+#include <thunar-vfs/thunar-vfs-job.h>
+#include <thunar-vfs/thunar-vfs-job-listdir.h>
+#include <thunar-vfs/thunar-vfs-mime.h>
 #include <thunar-vfs/thunar-vfs-monitor.h>
 #include <thunar-vfs/thunar-vfs-trash.h>
 #include <thunar-vfs/thunar-vfs-uri.h>
@@ -30,4 +33,10 @@
 #include <thunar-vfs/thunar-vfs-util.h>
 #include <thunar-vfs/thunar-vfs-volume.h>
 
+G_BEGIN_DECLS;
+
+void  thunar_vfs_init (void);
+
+G_END_DECLS;
+
 #endif /* !__THUNAR_VFS_H__ */
diff --git a/thunar-vfs/xdgmime/ChangeLog b/thunar-vfs/xdgmime/ChangeLog
new file mode 100644
index 0000000000000000000000000000000000000000..1197652361931716ef7f3dd3c710e3a2b448e8e1
--- /dev/null
+++ b/thunar-vfs/xdgmime/ChangeLog
@@ -0,0 +1,285 @@
+Thu Jun  9 23:55:25 2005  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmime.c (xdg_mime_init_from_directory): patch from
+	federico to realloc the right size, #3506
+
+2005-04-17  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/xdgmimeint.c: fix gcc4 signedness warning
+
+2005-04-17  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/xdgmimemagic.c: (_xdg_mime_magic_matchlet_compare_to_data),
+	(_xdg_mime_magic_matchlet_compare_level),
+	(_xdg_mime_magic_lookup_data): when  magic patterns matches, check if
+	there aren't subtypes with matching patterns too, and if so, favour
+	the subtype over the parent type, should fix #2686
+
+2005-04-16  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/xdgmime.c: (xdg_mime_init_from_directory): fix leak when
+	mime.cache doesn't exist
+
+2005-04-16  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/test-mime.c: (main): disabled call to xdg_mime_dump for now
+	* src/xdgmimemagic.c: (_xdg_mime_magic_matchlet_compare_to_data):
+	fixed off by 1 error when handling offsets, fixes bug #2050 and 
+	partly bug #2359
+
+Fri Apr  8 23:37:33 2005  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmimecache.c: Actually add the file.  Also, patch from
+	Matthias Clasen <mclasen@redhat.com> to fix small bugs, #2939
+
+Fri Apr  1 14:59:43 2005  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmimecache.c: Patch from Matthias Clasen to mmap the
+	cached xdg file.
+
+Mon Mar 28 13:58:32 2005  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmimeglob.c (_xdg_glob_hash_insert_text): patch from
+	Matthias Clasen to handle globs that don't have '.' chars in
+	them.  As an example 'foo~' should match '*~'
+
+Mon Mar 21 13:16:12 2005  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmime.c (xdg_mime_shutdown): fix from  Axel Liljencrantz
+	<f97-ali@nada.kth.se> to free parent_list in shutdown.
+
+2005-01-10  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/xdgmimeglob.c: (_xdg_glob_hash_lookup_file_name): make previous
+	commit actually work...
+
+2005-01-10  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/xdgmimeglob.c: (_xdg_glob_hash_lookup_file_name): don't get
+	confused by multiple dots in filenames when doing extension matching
+
+2004-12-13  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmime.h:
+
+	 wrap new API in XDG_ENTRY()
+
+2004-12-13  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimeglob.c: (_xdg_glob_hash_lookup_file_name):
+
+	Do not assume the filename is UTF8. We just need to look
+	for the dot which is ASCII.
+
+2004-12-09  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimeint.h:
+
+	Remove spacings I introduced by mistake
+
+2004-12-09  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimealias.c: (_xdg_mime_alias_read_from_file):
+	* src/xdgmimeint.c: (_xdg_ucs4_to_lower):
+	* src/xdgmimeint.h:
+	* src/xdgmimeparent.c: (_xdg_mime_parent_read_from_file):
+
+	Check in Mariano Suárez-Alvarez <msuarezalvarez@arnet.com.ar> patch
+	for GNOME bug #160838.
+
+2004-12-09  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimeglob.c: (_xdg_glob_hash_node_lookup_file_name):
+	* src/xdgmimeint.c: (_xdg_ucs4_to_lower):
+	* src/xdgmimeint.h:
+
+	Follow the freedesktop spec about case sensitiveness. Fix #732
+
+2004-12-08  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/xdgmimeglob.c: (_xdg_mime_glob_read_from_file): backing out
+	"fix" for bug #1048 since it frees memory that shouldn't be freed.
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimemagic.c: (_xdg_mime_magic_read_from_file):
+
+	Check that fread succeeded reading all chars. Fix #1049
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmime.c:
+	* src/xdgmimealias.c:
+	* src/xdgmimeglob.c:
+	* src/xdgmimeint.c:
+	* src/xdgmimemagic.c:
+	* src/xdgmimeparent.c:
+
+	Include config.h. Fix #913
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimealias.c: (_xdg_mime_alias_list_lookup):
+
+	Fix a typo
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmime.c: (xdg_mime_unalias_mime_type),
+	(xdg_mime_mime_type_equal), (xdg_mime_mime_type_subclass),
+	(xdg_mime_get_mime_parents):
+	* src/xdgmime.h:
+
+	Add apis to get parents and to unalias mime type
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimealias.c: (_xdg_mime_alias_list_lookup):
+	* src/xdgmimeparent.c: (_xdg_mime_parent_list_lookup):
+
+	Protect against stupid bsearch() implementations.  (#1961,
+	Morten Welinder)
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimeparent.c: (_xdg_mime_parent_read_from_file):
+
+	Initialize the parent field of the newly allocate list 
+	entry.  (#1916, Alex Larsson)
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimeglob.c: (_xdg_mime_glob_read_from_file):
+
+	Patch from Matthias Clasen <mclasen@redhat.com> to fix
+	a mem leak. Bug #1048
+
+2004-12-08  Marco Pesenti Gritti  <marco@gnome.org>
+
+	* src/xdgmimeglob.h:
+
+	Patch from Michael.Wilson@bull.net to fix compile error on AIX
+
+Sun Nov  7 02:25:21 2004  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmime.h: Patch from Matthias Clasen <mclasen@redhat.com>
+	to add alias and inheritence support.
+
+2004-09-16  Christophe Fergeau  <teuf@gnome.org>
+
+	* src/xdgmimeglob.c: (_xdg_glob_hash_free_nodes):
+	* src/xdgmimemagic.c: (_xdg_mime_magic_free): fix memory leaks, 
+	  fixes http://bugzilla.gnome.org/show_bug.cgi?id=152771 and
+	  http://bugzilla.gnome.org/show_bug.cgi?id=152768
+
+Mon Jul 19 00:23:00 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmime.c (xdg_mime_register_reload_callback): register a
+	callback when we reload MIME data.
+
+	* src/xdgmime.c (xdg_mime_remove_callback): Add capability to
+	remove callback.
+
+Sun Jul 18 20:56:22 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmime.c (xdg_mime_shutdown):
+	(xdg_mime_init): reread data when it changes on disk.
+
+Thu May 27 16:18:14 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmime.h: move xdg_mime_shutdown into the XDG_ENTRY guard.
+
+Thu May 27 15:02:13 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmimemagic.c (_xdg_mime_magic_read_magic_file): patch from
+	Hongli Lai <h.lai@chello.nl> to catch magic files that don't end
+	with a '\n'.
+
+Fri Apr 30 11:56:01 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmimemagic.c (_xdg_mime_magic_read_a_number): make the
+	buffer the right size.  Reported by Morten Welinder, #136323
+
+Sun Mar 21 23:56:46 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmimemagic.c: Patch from Arjan van de Ven
+	<arjanv@redhat.com> to do s/fgetc/getc_unlocked/g.
+
+Wed Mar 10 22:28:41 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmimemagic.c (_xdg_mime_magic_read_a_number): fix usage of
+	isdigit.  Reported by Morten Welinder, #136323
+
+	* src/xdgmimemagic.c (_xdg_mime_magic_read_magic_file): patch from
+	Christophe Fergeau to reverse the order of the matchlet before
+	adding it to the list.
+
+	* src/xdgmimeint.h (_xdg_utf8_skip): patch from Alexander Larsson
+	to make extern.
+
+Wed Jan 21 09:29:41 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmimemagic.c (_xdg_mime_magic_insert_match): dropped
+	patches.  Fix.
+
+	* src/xdgmimeglob.c (_xdg_glob_hash_free_nodes): dropped patches.
+	Fix.
+
+Tue Jan 20 14:55:39 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmime.h (XDG_MIME_TYPE_UNKNOWN): move the definition so
+	that it catches the XDG_ENTRY mangling.
+
+	* src/xdgmimemagic.c: make some functions static
+
+Tue Jan 20 14:34:26 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmime.c (xdg_mime_get_max_buffer_extents): add function so
+	that it's easy to get the max buffer extents.
+
+Tue Jan 20 12:55:55 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/Makefile: Test prefix code
+
+	* src/xdgmime*.h: Fully use the prefix code
+
+	* src/xdgmime.c: finish the syncing from both GTK+ and gnome-vfs.
+
+Tue Jan 13 16:21:04 2004  Jonathan Blandford  <jrb@gnome.org>
+
+	* src/xdgmime.[ch] (XDG_MIME_TYPE_UNKNOWN): make an extern const
+	char * so that comparisons can work.
+
+	* src/xdgmimeint.c (_xdg_utf8_to_ucs4): patch from Dave Jones
+	<davej@redhat.com> to make operations more explicit.
+
+Tue Oct 28 15:09:06 2003  Jonathan Blandford  <jrb@redhat.com>
+
+	* README: Add a readme, and clarify the licensing terms of the
+	software.
+
+Tue Oct 28 14:47:37 2003  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmime.c (xdg_mime_shutdown): implement shutdown.  This
+	frees all memory and resets to an uninitialized state as best as
+	possible.
+
+Mon Oct 27 11:45:58 2003  Jonathan Blandford  <jrb@redhat.com>
+
+	* src/xdgmimemagic.c (_xdg_mime_magic_read_a_number): strtol
+	returns a long, not an int.  Thanks to Manish Singh for pointing
+	this out.
+
+	* src/xdgmimemagic.c (_xdg_mime_magic_parse_magic_line): change
+	assertion to avoid a warning.
+
+Tue Oct 21 15:56:55 2003  Jonathan Blandford  <jrb@gnome.org>
+
+	* Makefile: add a simple makefile
+	* src/Makefile: ditto
+
+Tue Jul 22 15:37:45 2003  Jonathan Blandford  <jrb@gnome.org>
+
+	* xdgmime/xdgmime.c (xdg_mime_init): use XDG_DATA_HOME instead of
+	XDG_CONFIG_HOME.
+
diff --git a/thunar-vfs/xdgmime/Makefile.am b/thunar-vfs/xdgmime/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..92c4b8f185784db777977e2bf98cc1322702babb
--- /dev/null
+++ b/thunar-vfs/xdgmime/Makefile.am
@@ -0,0 +1,40 @@
+# $Id$
+
+INCLUDES =								\
+	-DXDG_PREFIX=_thunar_vfs_xdg
+
+noinst_LTLIBRARIES =							\
+	libxdgmime.la
+
+libxdgmime_la_SOURCES =							\
+	xdgmime.c							\
+	xdgmime.h							\
+	xdgmimealias.c							\
+	xdgmimealias.h							\
+	xdgmimecache.c							\
+	xdgmimecache.h							\
+	xdgmimeglob.c							\
+	xdgmimeglob.h							\
+	xdgmimeint.c							\
+	xdgmimeint.h							\
+	xdgmimemagic.c							\
+	xdgmimemagic.h							\
+	xdgmimeparent.c							\
+	xdgmimeparent.h
+
+TESTS =									\
+	test-mime
+
+check_PROGRAMS =							\
+	test-mime
+
+test_mime_SOURCES =							\
+	test-mime.c
+
+test_mime_DEPENDENCIES =						\
+	$(top_builddir)/thunar-vfs/xdgmime/libxdgmime.la
+
+test_mime_LDADD =							\
+	$(top_builddir)/thunar-vfs/xdgmime/libxdgmime.la
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/thunar-vfs/xdgmime/test-mime.c b/thunar-vfs/xdgmime/test-mime.c
new file mode 100644
index 0000000000000000000000000000000000000000..b0bcc875fe43159260e6d119276414fbe2639d59
--- /dev/null
+++ b/thunar-vfs/xdgmime/test-mime.c
@@ -0,0 +1,120 @@
+#include "xdgmime.h"
+#include "xdgmimeglob.h"
+#include <string.h>
+#include <stdio.h>
+
+
+static void
+test_individual_glob (const char  *glob,
+		      XdgGlobType  expected_type)
+{
+  XdgGlobType test_type;
+
+  test_type = _xdg_glob_determine_type (glob);
+  if (test_type != expected_type)
+    {
+      printf ("Test Failed: %s is of type %s, but %s is expected\n",
+	      glob,
+	      ((test_type == XDG_GLOB_LITERAL)?"XDG_GLOB_LITERAL":
+	       ((test_type == XDG_GLOB_SIMPLE)?"XDG_GLOB_SIMPLE":"XDG_GLOB_FULL")),
+	      ((expected_type == XDG_GLOB_LITERAL)?"XDG_GLOB_LITERAL":
+	       ((expected_type == XDG_GLOB_SIMPLE)?"XDG_GLOB_SIMPLE":"XDG_GLOB_COMPLEX")));
+    }
+}
+
+static void
+test_glob_type (void)
+{
+  test_individual_glob ("*.gif", XDG_GLOB_SIMPLE);
+  test_individual_glob ("Foo*.gif", XDG_GLOB_FULL);
+  test_individual_glob ("*[4].gif", XDG_GLOB_FULL);
+  test_individual_glob ("Makefile", XDG_GLOB_LITERAL);
+  test_individual_glob ("sldkfjvlsdf\\\\slkdjf", XDG_GLOB_FULL);
+  test_individual_glob ("tree.[ch]", XDG_GLOB_FULL);
+}
+
+static void
+test_alias (const char *mime_a,
+	    const char *mime_b,
+	    int         expected)
+{
+  int actual;
+
+  actual = xdg_mime_mime_type_equal (mime_a, mime_b);
+
+  if (actual != expected)
+    {
+      printf ("Test Failed: %s is %s to %s\n", 
+	      mime_a, actual ? "equal" : "not equal", mime_b);
+    }
+}
+
+static void
+test_aliasing (void)
+{
+  test_alias ("application/wordperfect", "application/vnd.wordperfect", 1);
+  test_alias ("application/x-gnome-app-info", "application/x-desktop", 1);
+  test_alias ("application/x-wordperfect", "application/vnd.wordperfect", 1);
+  test_alias ("application/x-wordperfect", "audio/x-midi", 0);
+  test_alias ("/", "vnd/vnd", 0);
+  test_alias ("application/octet-stream", "text/plain", 0);
+  test_alias ("text/plain", "text/*", 0);
+}
+
+static void
+test_subclass (const char *mime_a,
+	       const char *mime_b,
+	       int         expected)
+{
+  int actual;
+
+  actual = xdg_mime_mime_type_subclass (mime_a, mime_b);
+
+  if (actual != expected)
+    {
+      printf ("Test Failed: %s is %s of %s\n", 
+	      mime_a, actual ? "subclass" : "not subclass", mime_b);
+    }
+}
+
+static void
+test_subclassing (void)
+{
+  test_subclass ("application/rtf", "text/plain", 1);
+  test_subclass ("message/news", "text/plain", 1);
+  test_subclass ("message/news", "message/*", 1);
+  test_subclass ("message/news", "text/*", 1);
+  test_subclass ("message/news", "application/octet-stream", 1);
+  test_subclass ("application/rtf", "application/octet-stream", 1);
+  test_subclass ("application/x-gnome-app-info", "text/plain", 1);
+  test_subclass ("image/x-djvu", "image/vnd.djvu", 1);
+  test_subclass ("image/vnd.djvu", "image/x-djvu", 1);
+  test_subclass ("image/vnd.djvu", "text/plain", 0);
+  test_subclass ("image/vnd.djvu", "text/*", 0);
+  test_subclass ("text/*", "text/plain", 0);
+}
+
+int
+main (int argc, char *argv[])
+{
+  const char *result;
+  const char *file_name;
+  int i;
+
+  test_glob_type ();
+  test_aliasing ();
+  test_subclassing ();
+
+  for (i = 1; i < argc; i++)
+    {
+      file_name = argv[i];
+      result = xdg_mime_get_mime_type_for_file (file_name);
+      printf ("File \"%s\" has a mime-type of %s\n", file_name, result);
+    }
+
+#if 0
+  xdg_mime_dump ();
+#endif
+  return 0;
+}
+     
diff --git a/thunar-vfs/xdgmime/xdgmime.c b/thunar-vfs/xdgmime/xdgmime.c
new file mode 100644
index 0000000000000000000000000000000000000000..910c51210e318d6b1e8051ea18de154ebbc30888
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmime.c
@@ -0,0 +1,787 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.c: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ * 
+ * Copyright (C) 2003,2004  Red Hat, Inc.
+ * Copyright (C) 2003,2004  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 "xdgmime.h"
+#include "xdgmimeint.h"
+#include "xdgmimeglob.h"
+#include "xdgmimemagic.h"
+#include "xdgmimealias.h"
+#include "xdgmimeparent.h"
+#include "xdgmimecache.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+typedef struct XdgDirTimeList XdgDirTimeList;
+typedef struct XdgCallbackList XdgCallbackList;
+
+static int need_reread = TRUE;
+static time_t last_stat_time = 0;
+
+static XdgGlobHash *global_hash = NULL;
+static XdgMimeMagic *global_magic = NULL;
+static XdgAliasList *alias_list = NULL;
+static XdgParentList *parent_list = NULL;
+static XdgDirTimeList *dir_time_list = NULL;
+static XdgCallbackList *callback_list = NULL;
+XdgMimeCache **caches = NULL;
+int n_caches = 0;
+
+const char *xdg_mime_type_unknown = "application/octet-stream";
+
+
+enum
+{
+  XDG_CHECKED_UNCHECKED,
+  XDG_CHECKED_VALID,
+  XDG_CHECKED_INVALID
+};
+
+struct XdgDirTimeList
+{
+  time_t mtime;
+  char *directory_name;
+  int checked;
+  XdgDirTimeList *next;
+};
+
+struct XdgCallbackList
+{
+  XdgCallbackList *next;
+  XdgCallbackList *prev;
+  int              callback_id;
+  XdgMimeCallback  callback;
+  void            *data;
+  XdgMimeDestroy   destroy;
+};
+
+/* Function called by xdg_run_command_on_dirs.  If it returns TRUE, further
+ * directories aren't looked at */
+typedef int (*XdgDirectoryFunc) (const char *directory,
+				 void       *user_data);
+
+static XdgDirTimeList *
+xdg_dir_time_list_new (void)
+{
+  XdgDirTimeList *retval;
+
+  retval = calloc (1, sizeof (XdgDirTimeList));
+  retval->checked = XDG_CHECKED_UNCHECKED;
+
+  return retval;
+}
+
+static void
+xdg_dir_time_list_free (XdgDirTimeList *list)
+{
+  XdgDirTimeList *next;
+
+  while (list)
+    {
+      next = list->next;
+      free (list->directory_name);
+      free (list);
+      list = next;
+    }
+}
+
+static int
+xdg_mime_init_from_directory (const char *directory)
+{
+  char *file_name;
+  struct stat st;
+  XdgDirTimeList *list;
+
+  assert (directory != NULL);
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
+  if (stat (file_name, &st) == 0)
+    {
+      XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
+
+      if (cache != NULL)
+	{
+	  list = xdg_dir_time_list_new ();
+	  list->directory_name = file_name;
+	  list->mtime = st.st_mtime;
+	  list->next = dir_time_list;
+	  dir_time_list = list;
+
+	  caches = realloc (caches, sizeof (XdgMimeCache *) * (n_caches + 1));
+	  caches[n_caches] = cache;
+	  n_caches++;
+
+	  return FALSE;
+	}
+    }
+  free (file_name);
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/globs");
+  if (stat (file_name, &st) == 0)
+    {
+      _xdg_mime_glob_read_from_file (global_hash, file_name);
+
+      list = xdg_dir_time_list_new ();
+      list->directory_name = file_name;
+      list->mtime = st.st_mtime;
+      list->next = dir_time_list;
+      dir_time_list = list;
+    }
+  else
+    {
+      free (file_name);
+    }
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/magic");
+  if (stat (file_name, &st) == 0)
+    {
+      _xdg_mime_magic_read_from_file (global_magic, file_name);
+
+      list = xdg_dir_time_list_new ();
+      list->directory_name = file_name;
+      list->mtime = st.st_mtime;
+      list->next = dir_time_list;
+      dir_time_list = list;
+    }
+  else
+    {
+      free (file_name);
+    }
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
+  _xdg_mime_alias_read_from_file (alias_list, file_name);
+  free (file_name);
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
+  _xdg_mime_parent_read_from_file (parent_list, file_name);
+  free (file_name);
+
+  return FALSE; /* Keep processing */
+}
+
+/* Runs a command on all the directories in the search path */
+static void
+xdg_run_command_on_dirs (XdgDirectoryFunc  func,
+			 void             *user_data)
+{
+  const char *xdg_data_home;
+  const char *xdg_data_dirs;
+  const char *ptr;
+
+  xdg_data_home = getenv ("XDG_DATA_HOME");
+  if (xdg_data_home)
+    {
+      if ((func) (xdg_data_home, user_data))
+	return;
+    }
+  else
+    {
+      const char *home;
+
+      home = getenv ("HOME");
+      if (home != NULL)
+	{
+	  char *guessed_xdg_home;
+	  int stop_processing;
+
+	  guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
+	  strcpy (guessed_xdg_home, home);
+	  strcat (guessed_xdg_home, "/.local/share/");
+	  stop_processing = (func) (guessed_xdg_home, user_data);
+	  free (guessed_xdg_home);
+
+	  if (stop_processing)
+	    return;
+	}
+    }
+
+  xdg_data_dirs = getenv ("XDG_DATA_DIRS");
+  if (xdg_data_dirs == NULL)
+    xdg_data_dirs = "/usr/local/share/:/usr/share/";
+
+  ptr = xdg_data_dirs;
+
+  while (*ptr != '\000')
+    {
+      const char *end_ptr;
+      char *dir;
+      int len;
+      int stop_processing;
+
+      end_ptr = ptr;
+      while (*end_ptr != ':' && *end_ptr != '\000')
+	end_ptr ++;
+
+      if (end_ptr == ptr)
+	{
+	  ptr++;
+	  continue;
+	}
+
+      if (*end_ptr == ':')
+	len = end_ptr - ptr;
+      else
+	len = end_ptr - ptr + 1;
+      dir = malloc (len + 1);
+      strncpy (dir, ptr, len);
+      dir[len] = '\0';
+      stop_processing = (func) (dir, user_data);
+      free (dir);
+
+      if (stop_processing)
+	return;
+
+      ptr = end_ptr;
+    }
+}
+
+/* Checks file_path to make sure it has the same mtime as last time it was
+ * checked.  If it has a different mtime, or if the file doesn't exist, it
+ * returns FALSE.
+ *
+ * FIXME: This doesn't protect against permission changes.
+ */
+static int
+xdg_check_file (const char *file_path)
+{
+  struct stat st;
+
+  /* If the file exists */
+  if (stat (file_path, &st) == 0)
+    {
+      XdgDirTimeList *list;
+
+      for (list = dir_time_list; list; list = list->next)
+	{
+	  if (! strcmp (list->directory_name, file_path) &&
+	      st.st_mtime == list->mtime)
+	    {
+	      if (list->checked == XDG_CHECKED_UNCHECKED)
+		list->checked = XDG_CHECKED_VALID;
+	      else if (list->checked == XDG_CHECKED_VALID)
+		list->checked = XDG_CHECKED_INVALID;
+
+	      return (list->checked != XDG_CHECKED_VALID);
+	    }
+	}
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static int
+xdg_check_dir (const char *directory,
+	       int        *invalid_dir_list)
+{
+  int invalid;
+  char *file_name;
+
+  assert (directory != NULL);
+
+  /* Check the globs file */
+  file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/globs");
+  invalid = xdg_check_file (file_name);
+  free (file_name);
+  if (invalid)
+    {
+      *invalid_dir_list = TRUE;
+      return TRUE;
+    }
+
+  /* Check the magic file */
+  file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/magic");
+  invalid = xdg_check_file (file_name);
+  free (file_name);
+  if (invalid)
+    {
+      *invalid_dir_list = TRUE;
+      return TRUE;
+    }
+
+  /* Check the mime.cache file */
+  file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
+  invalid = xdg_check_file (file_name);
+  free (file_name);
+  if (invalid)
+    {
+      *invalid_dir_list = TRUE;
+      return TRUE;
+    }
+
+  return FALSE; /* Keep processing */
+}
+
+/* Walks through all the mime files stat()ing them to see if they've changed.
+ * Returns TRUE if they have. */
+static int
+xdg_check_dirs (void)
+{
+  XdgDirTimeList *list;
+  int invalid_dir_list = FALSE;
+
+  for (list = dir_time_list; list; list = list->next)
+    list->checked = XDG_CHECKED_UNCHECKED;
+
+  xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
+			   &invalid_dir_list);
+
+  if (invalid_dir_list)
+    return TRUE;
+
+  for (list = dir_time_list; list; list = list->next)
+    {
+      if (list->checked != XDG_CHECKED_VALID)
+	return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* We want to avoid stat()ing on every single mime call, so we only look for
+ * newer files every 5 seconds.  This will return TRUE if we need to reread the
+ * mime data from disk.
+ */
+static int
+xdg_check_time_and_dirs (void)
+{
+  struct timeval tv;
+  time_t current_time;
+  int retval = FALSE;
+
+  gettimeofday (&tv, NULL);
+  current_time = tv.tv_sec;
+
+  if (current_time >= last_stat_time + 60)
+    {
+      retval = xdg_check_dirs ();
+      last_stat_time = current_time;
+    }
+
+  return retval;
+}
+
+/* Called in every public function.  It reloads the hash function if need be.
+ */
+void
+xdg_mime_init (void)
+{
+  if (xdg_check_time_and_dirs ())
+    {
+      xdg_mime_shutdown ();
+    }
+
+  if (need_reread)
+    {
+      global_hash = _xdg_glob_hash_new ();
+      global_magic = _xdg_mime_magic_new ();
+      alias_list = _xdg_mime_alias_list_new ();
+      parent_list = _xdg_mime_parent_list_new ();
+
+      xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
+			       NULL);
+
+      need_reread = FALSE;
+    }
+}
+
+const char *
+xdg_mime_get_mime_type_for_data (const void *data,
+				 size_t      len)
+{
+  const char *mime_type;
+
+  xdg_mime_init ();
+
+  if (caches)
+    return _xdg_mime_cache_get_mime_type_for_data (data, len);
+
+  mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len);
+
+  if (mime_type)
+    return mime_type;
+
+  return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+xdg_mime_get_mime_type_for_file (const char *file_name)
+{
+  const char *mime_type;
+  int fd;
+  unsigned char *data;
+  int max_extent;
+  int bytes_read;
+  const char *base_name;
+
+  if (file_name == NULL)
+    return NULL;
+  if (! _xdg_utf8_validate (file_name))
+    return NULL;
+
+  xdg_mime_init ();
+
+  if (caches)
+    return _xdg_mime_cache_get_mime_type_for_file (file_name);
+
+  base_name = _xdg_get_base_name (file_name);
+  mime_type = xdg_mime_get_mime_type_from_file_name (base_name);
+
+  if (mime_type != XDG_MIME_TYPE_UNKNOWN)
+    return mime_type;
+
+  /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
+   * be large and need getting from a stream instead of just reading it all
+   * in. */
+  max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
+  data = malloc (max_extent);
+  if (data == NULL)
+    return XDG_MIME_TYPE_UNKNOWN;
+
+  fd = open (file_name, O_RDONLY, 0);
+  if (fd < 0)
+    {
+      free (data);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  bytes_read = read (fd, data, max_extent);
+
+  close (fd);
+
+  if (bytes_read <= 0)
+    {
+      free (data);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read);
+
+  free (data);
+
+  if (mime_type)
+    return mime_type;
+
+  return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+xdg_mime_get_mime_type_from_file_name (const char *file_name)
+{
+  const char *mime_type;
+
+  xdg_mime_init ();
+
+  if (caches)
+    return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
+
+  mime_type = _xdg_glob_hash_lookup_file_name (global_hash, file_name);
+  if (mime_type)
+    return mime_type;
+  else
+    return XDG_MIME_TYPE_UNKNOWN;
+}
+
+int
+xdg_mime_is_valid_mime_type (const char *mime_type)
+{
+  /* FIXME: We should make this a better test
+   */
+  return _xdg_utf8_validate (mime_type);
+}
+
+void
+xdg_mime_shutdown (void)
+{
+  XdgCallbackList *list;
+
+  /* FIXME: Need to make this (and the whole library) thread safe */
+  if (dir_time_list)
+    {
+      xdg_dir_time_list_free (dir_time_list);
+      dir_time_list = NULL;
+    }
+	
+  if (global_hash)
+    {
+      _xdg_glob_hash_free (global_hash);
+      global_hash = NULL;
+    }
+  if (global_magic)
+    {
+      _xdg_mime_magic_free (global_magic);
+      global_magic = NULL;
+    }
+
+  if (alias_list)
+    {
+      _xdg_mime_alias_list_free (alias_list);
+      alias_list = NULL;
+    }
+
+  if (parent_list)
+    {
+      _xdg_mime_parent_list_free (parent_list);
+      parent_list = NULL;
+    }
+  
+  for (list = callback_list; list; list = list->next)
+    (list->callback) (list->data);
+
+  need_reread = TRUE;
+}
+
+int
+xdg_mime_get_max_buffer_extents (void)
+{
+  xdg_mime_init ();
+  
+  if (caches)
+    return _xdg_mime_cache_get_max_buffer_extents ();
+
+  return _xdg_mime_magic_get_buffer_extents (global_magic);
+}
+
+const char *
+xdg_mime_unalias_mime_type (const char *mime_type)
+{
+  const char *lookup;
+
+  xdg_mime_init ();
+
+  if (caches)
+    return _xdg_mime_cache_unalias_mime_type (mime_type);
+
+  if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
+    return lookup;
+
+  return mime_type;
+}
+
+int
+xdg_mime_mime_type_equal (const char *mime_a,
+			  const char *mime_b)
+{
+  const char *unalias_a, *unalias_b;
+
+  xdg_mime_init ();
+
+  unalias_a = xdg_mime_unalias_mime_type (mime_a);
+  unalias_b = xdg_mime_unalias_mime_type (mime_b);
+
+  if (strcmp (unalias_a, unalias_b) == 0)
+    return 1;
+
+  return 0;
+}
+
+int
+xdg_mime_media_type_equal (const char *mime_a,
+			   const char *mime_b)
+{
+  char *sep;
+
+  xdg_mime_init ();
+
+  sep = strchr (mime_a, '/');
+  
+  if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
+    return 1;
+
+  return 0;
+}
+
+#if 0
+static int
+xdg_mime_is_super_type (const char *mime)
+{
+  int length;
+  const char *type;
+
+  length = strlen (mime);
+  type = &(mime[length - 2]);
+
+  if (strcmp (type, "/*") == 0)
+    return 1;
+
+  return 0;
+}
+#endif
+
+int
+xdg_mime_mime_type_subclass (const char *mime,
+			     const char *base)
+{
+  const char *umime, *ubase;
+  const char **parents;
+
+  xdg_mime_init ();
+
+  if (caches)
+    return _xdg_mime_cache_mime_type_subclass (mime, base);
+
+  umime = xdg_mime_unalias_mime_type (mime);
+  ubase = xdg_mime_unalias_mime_type (base);
+
+  if (strcmp (umime, ubase) == 0)
+    return 1;
+
+#if 0  
+  /* Handle supertypes */
+  if (xdg_mime_is_super_type (ubase) &&
+      xdg_mime_media_type_equal (umime, ubase))
+    return 1;
+#endif
+
+  /*  Handle special cases text/plain and application/octet-stream */
+  if (strcmp (ubase, "text/plain") == 0 && 
+      strncmp (umime, "text/", 5) == 0)
+    return 1;
+
+  if (strcmp (ubase, "application/octet-stream") == 0)
+    return 1;
+  
+  parents = _xdg_mime_parent_list_lookup (parent_list, umime);
+  for (; parents && *parents; parents++)
+    {
+      if (xdg_mime_mime_type_subclass (*parents, ubase))
+	return 1;
+    }
+
+  return 0;
+}
+
+char **
+xdg_mime_list_mime_parents (const char *mime)
+{
+  const char **parents;
+  char **result;
+  int i, n;
+
+  if (caches)
+    return _xdg_mime_cache_list_mime_parents (mime);
+
+  parents = xdg_mime_get_mime_parents (mime);
+  for (i = 0; parents[i]; i++) ;
+  
+  n = (i + 1) * sizeof (char *);
+  result = (char **) malloc (n);
+  memcpy (result, parents, n);
+
+  return result;
+}
+
+const char **
+xdg_mime_get_mime_parents (const char *mime)
+{
+  const char *umime;
+
+  xdg_mime_init ();
+
+  umime = xdg_mime_unalias_mime_type (mime);
+
+  return _xdg_mime_parent_list_lookup (parent_list, umime);
+}
+
+void 
+xdg_mime_dump (void)
+{
+  printf ("*** ALIASES ***\n\n");
+  _xdg_mime_alias_list_dump (alias_list);
+  printf ("\n*** PARENTS ***\n\n");
+  _xdg_mime_parent_list_dump (parent_list);
+}
+
+
+/* Registers a function to be called every time the mime database reloads its files
+ */
+int
+xdg_mime_register_reload_callback (XdgMimeCallback  callback,
+				   void            *data,
+				   XdgMimeDestroy   destroy)
+{
+  XdgCallbackList *list_el;
+  static int callback_id = 1;
+
+  /* Make a new list element */
+  list_el = calloc (1, sizeof (XdgCallbackList));
+  list_el->callback_id = callback_id;
+  list_el->callback = callback;
+  list_el->data = data;
+  list_el->destroy = destroy;
+  list_el->next = callback_list;
+  if (list_el->next)
+    list_el->next->prev = list_el;
+
+  callback_list = list_el;
+  callback_id ++;
+
+  return callback_id - 1;
+}
+
+void
+xdg_mime_remove_callback (int callback_id)
+{
+  XdgCallbackList *list;
+
+  for (list = callback_list; list; list = list->next)
+    {
+      if (list->callback_id == callback_id)
+	{
+	  if (list->next)
+	    list->next = list->prev;
+
+	  if (list->prev)
+	    list->prev->next = list->next;
+	  else
+	    callback_list = list->next;
+
+	  /* invoke the destroy handler */
+	  if (list->destroy != NULL)
+	    (list->destroy) (list->data);
+	  free (list);
+	  return;
+	}
+    }
+}
diff --git a/thunar-vfs/xdgmime/xdgmime.h b/thunar-vfs/xdgmime/xdgmime.h
new file mode 100644
index 0000000000000000000000000000000000000000..f4041ea57dd7ec18da16b8290619a45267945916
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmime.h
@@ -0,0 +1,101 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.h: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ * 
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __XDG_MIME_H__
+#define __XDG_MIME_H__
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef XDG_PREFIX
+#define XDG_ENTRY(func) _XDG_ENTRY2(XDG_PREFIX,func)
+#define _XDG_ENTRY2(prefix,func) _XDG_ENTRY3(prefix,func)
+#define _XDG_ENTRY3(prefix,func) prefix##_##func
+#endif
+
+typedef void (*XdgMimeCallback) (void *user_data);
+typedef void (*XdgMimeDestroy)  (void *user_data);
+
+  
+#ifdef XDG_PREFIX
+#define xdg_mime_get_mime_type_for_data       XDG_ENTRY(get_mime_type_for_data)
+#define xdg_mime_get_mime_type_for_file       XDG_ENTRY(get_mime_type_for_file)
+#define xdg_mime_get_mime_type_from_file_name XDG_ENTRY(get_mime_type_from_file_name)
+#define xdg_mime_is_valid_mime_type           XDG_ENTRY(is_valid_mime_type)
+#define xdg_mime_mime_type_equal              XDG_ENTRY(mime_type_equal)
+#define xdg_mime_media_type_equal             XDG_ENTRY(media_type_equal)
+#define xdg_mime_mime_type_subclass           XDG_ENTRY(mime_type_subclass)
+#define xdg_mime_get_mime_parents             XDG_ENTRY(get_mime_parents)
+#define xdg_mime_list_mime_parents            XDG_ENTRY(list_mime_parents)
+#define xdg_mime_unalias_mime_type            XDG_ENTRY(unalias_mime_type)
+#define xdg_mime_get_max_buffer_extents       XDG_ENTRY(get_max_buffer_extents)
+#define xdg_mime_shutdown                     XDG_ENTRY(shutdown)
+#define xdg_mime_register_reload_callback     XDG_ENTRY(register_reload_callback)
+#define xdg_mime_remove_callback              XDG_ENTRY(remove_callback)
+#define xdg_mime_type_unknown                 XDG_ENTRY(type_unknown)
+#endif
+
+extern const char *xdg_mime_type_unknown;
+#define XDG_MIME_TYPE_UNKNOWN xdg_mime_type_unknown
+
+void         xdg_mime_init                         (void);
+const char  *xdg_mime_get_mime_type_for_data       (const void *data,
+						    size_t      len);
+const char  *xdg_mime_get_mime_type_for_file       (const char *file_name);
+const char  *xdg_mime_get_mime_type_from_file_name (const char *file_name);
+int          xdg_mime_is_valid_mime_type           (const char *mime_type);
+int          xdg_mime_mime_type_equal              (const char *mime_a,
+						    const char *mime_b);
+int          xdg_mime_media_type_equal             (const char *mime_a,
+						    const char *mime_b);
+int          xdg_mime_mime_type_subclass           (const char *mime_a,
+						    const char *mime_b);
+  /* xdg_mime_get_mime_parents() is deprecated since it does
+   * not work correctly with caches. Use xdg_mime_list_parents() 
+   * instead, but notice that that function expects you to free
+   * the array it returns. 
+   */
+const char **xdg_mime_get_mime_parents		   (const char *mime);
+char **      xdg_mime_list_mime_parents		   (const char *mime);
+const char  *xdg_mime_unalias_mime_type		   (const char *mime);
+int          xdg_mime_get_max_buffer_extents       (void);
+void         xdg_mime_shutdown                     (void);
+void         xdg_mime_dump                         (void);
+int          xdg_mime_register_reload_callback     (XdgMimeCallback  callback,
+						    void            *data,
+						    XdgMimeDestroy   destroy);
+void         xdg_mime_remove_callback              (int              callback_id);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __XDG_MIME_H__ */
diff --git a/thunar-vfs/xdgmime/xdgmimealias.c b/thunar-vfs/xdgmime/xdgmimealias.c
new file mode 100644
index 0000000000000000000000000000000000000000..2dd70f1d6987f6c3ce1fb8822e2005c67e80b23c
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimealias.c
@@ -0,0 +1,184 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file.  Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 2004  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 "xdgmimealias.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef	FALSE
+#define	FALSE	(0)
+#endif
+
+#ifndef	TRUE
+#define	TRUE	(!FALSE)
+#endif
+
+typedef struct XdgAlias XdgAlias;
+
+struct XdgAlias 
+{
+  char *alias;
+  char *mime_type;
+};
+
+struct XdgAliasList
+{
+  struct XdgAlias *aliases;
+  int n_aliases;
+};
+
+XdgAliasList *
+_xdg_mime_alias_list_new (void)
+{
+  XdgAliasList *list;
+
+  list = malloc (sizeof (XdgAliasList));
+
+  list->aliases = NULL;
+  list->n_aliases = 0;
+
+  return list;
+}
+
+void         
+_xdg_mime_alias_list_free (XdgAliasList *list)
+{
+  int i;
+
+  if (list->aliases)
+    {
+      for (i = 0; i < list->n_aliases; i++)
+	{
+	  free (list->aliases[i].alias);
+	  free (list->aliases[i].mime_type);
+	}
+      free (list->aliases);
+    }
+  free (list);
+}
+
+static int
+alias_entry_cmp (const void *v1, const void *v2)
+{
+  return strcmp (((XdgAlias *)v1)->alias, ((XdgAlias *)v2)->alias);
+}
+
+const char  *
+_xdg_mime_alias_list_lookup (XdgAliasList *list,
+			     const char   *alias)
+{
+  XdgAlias *entry;
+  XdgAlias key;
+
+  if (list->n_aliases > 0)
+    {
+      key.alias = (char *)alias;
+      key.mime_type = 0;
+
+      entry = bsearch (&key, list->aliases, list->n_aliases,
+		       sizeof (XdgAlias), alias_entry_cmp);
+      if (entry)
+        return entry->mime_type;
+    }
+
+  return NULL;
+}
+
+void
+_xdg_mime_alias_read_from_file (XdgAliasList *list,
+				const char   *file_name)
+{
+  FILE *file;
+  char line[255];
+  int alloc;
+
+  file = fopen (file_name, "r");
+
+  if (file == NULL)
+    return;
+
+  /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
+   * Blah */
+  alloc = list->n_aliases + 16;
+  list->aliases = realloc (list->aliases, alloc * sizeof (XdgAlias));
+  while (fgets (line, 255, file) != NULL)
+    {
+      char *sep;
+      if (line[0] == '#')
+	continue;
+
+      sep = strchr (line, ' ');
+      if (sep == NULL)
+	continue;
+      *(sep++) = '\000';
+      sep[strlen (sep) -1] = '\000';
+      if (list->n_aliases == alloc)
+	{
+	  alloc <<= 1;
+	  list->aliases = realloc (list->aliases, 
+				   alloc * sizeof (XdgAlias));
+	}
+      list->aliases[list->n_aliases].alias = strdup (line);
+      list->aliases[list->n_aliases].mime_type = strdup (sep);
+      list->n_aliases++;
+    }
+  list->aliases = realloc (list->aliases, 
+			   list->n_aliases * sizeof (XdgAlias));
+
+  fclose (file);  
+  
+  if (list->n_aliases > 1)
+    qsort (list->aliases, list->n_aliases, 
+           sizeof (XdgAlias), alias_entry_cmp);
+}
+
+
+void
+_xdg_mime_alias_list_dump (XdgAliasList *list)
+{
+  int i;
+
+  if (list->aliases)
+    {
+      for (i = 0; i < list->n_aliases; i++)
+	{
+	  printf ("%s %s\n", 
+		  list->aliases[i].alias,
+		  list->aliases[i].mime_type);
+	}
+    }
+}
+
+
diff --git a/thunar-vfs/xdgmime/xdgmimealias.h b/thunar-vfs/xdgmime/xdgmimealias.h
new file mode 100644
index 0000000000000000000000000000000000000000..3df18d66067726c25426b779c31fe1ed355059b0
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimealias.h
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.h: Private file.  Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 200  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_ALIAS_H__
+#define __XDG_MIME_ALIAS_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgAliasList XdgAliasList;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_alias_read_from_file        XDG_ENTRY(alias_read_from_file)
+#define _xdg_mime_alias_list_new              XDG_ENTRY(alias_list_new)
+#define _xdg_mime_alias_list_free             XDG_ENTRY(alias_list_free)
+#define _xdg_mime_alias_list_lookup           XDG_ENTRY(alias_list_lookup)
+#endif
+
+void          _xdg_mime_alias_read_from_file (XdgAliasList *list,
+					      const char   *file_name);
+XdgAliasList *_xdg_mime_alias_list_new       (void);
+void          _xdg_mime_alias_list_free      (XdgAliasList *list);
+const char   *_xdg_mime_alias_list_lookup    (XdgAliasList *list,
+					      const char  *alias);
+void          _xdg_mime_alias_list_dump      (XdgAliasList *list);
+
+#endif /* __XDG_MIME_ALIAS_H__ */
diff --git a/thunar-vfs/xdgmime/xdgmimecache.c b/thunar-vfs/xdgmime/xdgmimecache.c
new file mode 100644
index 0000000000000000000000000000000000000000..bfa02862b559cbd243bffe50b46966432d5f27ee
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimecache.c
@@ -0,0 +1,811 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file.  mmappable caches for mime data
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2005  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#include <assert.h>
+
+#include <netinet/in.h> /* for ntohl/ntohs */
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "xdgmimecache.h"
+#include "xdgmimeint.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef	FALSE
+#define	FALSE	(0)
+#endif
+
+#ifndef	TRUE
+#define	TRUE	(!FALSE)
+#endif
+
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 0
+
+extern XdgMimeCache **caches;
+extern int n_caches;
+
+struct _XdgMimeCache
+{
+  int ref_count;
+
+  size_t  size;
+  char   *buffer;
+};
+
+#define GET_UINT16(cache,offset) (ntohs(*(uint16_t*)((cache) + (offset))))
+#define GET_UINT32(cache,offset) (ntohl(*(uint32_t*)((cache) + (offset))))
+
+XdgMimeCache *
+_xdg_mime_cache_ref (XdgMimeCache *cache)
+{
+  cache->ref_count++;
+  return cache;
+}
+
+void
+_xdg_mime_cache_unref (XdgMimeCache *cache)
+{
+  cache->ref_count--;
+
+  if (cache->ref_count == 0)
+    {
+#ifdef HAVE_MMAP
+      munmap (cache->buffer, cache->size);
+#endif
+      free (cache);
+    }
+}
+
+XdgMimeCache *
+_xdg_mime_cache_new_from_file (const char *file_name)
+{
+  XdgMimeCache *cache = NULL;
+
+#ifdef HAVE_MMAP
+  int fd = -1;
+  struct stat st;
+  char *buffer = NULL;
+
+  /* Open the file and map it into memory */
+  fd = open (file_name, O_RDONLY|_O_BINARY, 0);
+
+  if (fd < 0)
+    return NULL;
+  
+  if (fstat (fd, &st) < 0 || st.st_size < 4)
+    goto done;
+
+  buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+
+  if (buffer == MAP_FAILED)
+    goto done;
+
+  /* Verify version */
+  if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
+      GET_UINT16 (buffer, 2) != MINOR_VERSION)
+    {
+      munmap (buffer, st.st_size);
+
+      goto done;
+    }
+  
+  cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
+  cache->ref_count = 1;
+  cache->buffer = buffer;
+  cache->size = st.st_size;
+
+ done:
+  if (fd != -1)
+    close (fd);
+
+#endif  /* HAVE_MMAP */
+
+  return cache;
+}
+
+static int
+cache_magic_matchlet_compare_to_data (XdgMimeCache *cache, 
+				      xdg_uint32_t  offset,
+				      const void   *data,
+				      size_t        len)
+{
+  xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
+  xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
+  xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
+  xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
+  xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
+  
+  int i, j;
+
+  for (i = range_start; i <= range_start + range_length; i++)
+    {
+      int valid_matchlet = TRUE;
+      
+      if (i + data_length > len)
+	return FALSE;
+
+      if (mask_offset)
+	{
+	  for (j = 0; j < data_length; j++)
+	    {
+	      if ((cache->buffer[data_offset + j] & cache->buffer[mask_offset + j]) !=
+		  ((((unsigned char *) data)[j + i]) & cache->buffer[mask_offset + j]))
+		{
+		  valid_matchlet = FALSE;
+		  break;
+		}
+	    }
+	}
+      else
+	{
+	  for (j = 0; j < data_length; j++)
+	    {
+	      if (cache->buffer[data_offset + j] != ((unsigned char *) data)[j + i])
+		{
+		  valid_matchlet = FALSE;
+		  break;
+		}
+	    }
+	}
+      
+      if (valid_matchlet)
+	return TRUE;
+    }
+  
+  return FALSE;  
+}
+
+static int
+cache_magic_matchlet_compare (XdgMimeCache *cache, 
+			      xdg_uint32_t  offset,
+			      const void   *data,
+			      size_t        len)
+{
+  xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
+  xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
+
+  int i;
+  
+  if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
+    {
+      if (n_children == 0)
+	return TRUE;
+      
+      for (i = 0; i < n_children; i++)
+	{
+	  if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
+					    data, len))
+	    return TRUE;
+	}
+    }
+  
+  return FALSE;  
+}
+
+static const char *
+cache_magic_compare_to_data (XdgMimeCache *cache, 
+			     xdg_uint32_t  offset,
+			     const void   *data, 
+			     size_t        len, 
+			     int          *prio)
+{
+  xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
+  xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
+  xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
+  xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
+
+  int i;
+
+  for (i = 0; i < n_matchlets; i++)
+    {
+      if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32, 
+					data, len))
+	{
+	  *prio = priority;
+	  
+	  return cache->buffer + mimetype_offset;
+	}
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_magic_lookup_data (XdgMimeCache *cache, 
+			 const void   *data, 
+			 size_t        len, 
+			 int          *prio)
+{
+  xdg_uint32_t list_offset;
+  xdg_uint32_t n_entries;
+  xdg_uint32_t offset;
+
+  int j;
+
+  *prio = 0;
+
+  list_offset = GET_UINT32 (cache->buffer, 24);
+  n_entries = GET_UINT32 (cache->buffer, list_offset);
+  offset = GET_UINT32 (cache->buffer, list_offset + 8);
+  
+  for (j = 0; j < n_entries; j++)
+    {
+      const char *match = cache_magic_compare_to_data (cache, offset + 16 * j, 
+						       data, len, prio);
+      if (match)
+	return match;
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_alias_lookup (const char *alias)
+{
+  const char *ptr;
+  int i, min, max, mid, cmp;
+
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4 );
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset;
+
+      min = 0; 
+      max = n_entries - 1;
+      while (max >= min) 
+	{
+	  mid = (min + max) / 2;
+
+	  offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
+	  ptr = cache->buffer + offset;
+	  cmp = strcmp (ptr, alias);
+	  
+	  if (cmp < 0)
+	    min = mid + 1;
+	  else if (cmp > 0)
+	    max = mid - 1;
+	  else
+	    {
+	      offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
+	      return cache->buffer + offset;
+	    }
+	}
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_glob_lookup_literal (const char *file_name)
+{
+  const char *ptr;
+  int i, min, max, mid, cmp;
+
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset;
+
+      min = 0; 
+      max = n_entries - 1;
+      while (max >= min) 
+	{
+	  mid = (min + max) / 2;
+
+	  offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
+	  ptr = cache->buffer + offset;
+	  cmp = strcmp (ptr, file_name);
+	  
+	  if (cmp < 0)
+	    min = mid + 1;
+	  else if (cmp > 0)
+	    max = mid - 1;
+	  else
+	    {
+	      offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
+	      return  cache->buffer + offset;
+	    }
+	}
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_glob_lookup_fnmatch (const char *file_name)
+{
+  const char *mime_type;
+  const char *ptr;
+
+  int i, j;
+
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+
+      for (j = 0; j < n_entries; j++)
+	{
+	  xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
+	  xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
+	  ptr = cache->buffer + offset;
+	  mime_type = cache->buffer + mimetype_offset;
+
+	  /* FIXME: Not UTF-8 safe */
+	  if (fnmatch (ptr, file_name, 0) == 0)
+	    return mime_type;
+	}
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_glob_node_lookup_suffix (XdgMimeCache *cache,
+			       xdg_uint32_t  n_entries,
+			       xdg_uint32_t  offset,
+			       const char   *suffix, 
+			       int           ignore_case)
+{
+  xdg_unichar_t character;
+  xdg_unichar_t match_char;
+  xdg_uint32_t mimetype_offset;
+  xdg_uint32_t n_children;
+  xdg_uint32_t child_offset; 
+
+  int min, max, mid;
+
+  character = _xdg_utf8_to_ucs4 (suffix);
+  if (ignore_case)
+    character = _xdg_ucs4_to_lower (character);
+
+  min = 0;
+  max = n_entries - 1;
+  while (max >= min)
+    {
+      mid = (min + max) /  2;
+
+      match_char = GET_UINT32 (cache->buffer, offset + 16 * mid);
+
+      if (match_char < character)
+	min = mid + 1;
+      else if (match_char > character)
+	max = mid - 1;
+      else 
+	{
+	  suffix = _xdg_utf8_next_char (suffix);
+	  if (*suffix == '\0')
+	    {
+	      mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 4);
+
+	      return cache->buffer + mimetype_offset;
+	    }
+	  else
+	    {
+	      n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
+	      child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
+      
+	      return cache_glob_node_lookup_suffix (cache, 
+						    n_children, child_offset,
+						    suffix, ignore_case);
+	    }
+	}
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_glob_lookup_suffix (const char *suffix, 
+			  int         ignore_case)
+{
+  const char *mime_type;
+
+  int i;
+
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
+
+      mime_type = cache_glob_node_lookup_suffix (cache, 
+						 n_entries, offset, 
+						 suffix, ignore_case);
+      if (mime_type)
+	return mime_type;
+    }
+
+  return NULL;
+}
+
+static void
+find_stopchars (char *stopchars)
+{
+  int i, j, k, l;
+ 
+  k = 0;
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
+
+      for (j = 0; j < n_entries; j++)
+	{
+	  xdg_uint32_t match_char = GET_UINT32 (cache->buffer, offset);
+	  
+	  if (match_char < 128)
+	    {
+	      for (l = 0; l < k; l++)
+		if (stopchars[l] == match_char)
+		  break;
+	      if (l == k)
+		{
+		  stopchars[k] = (char) match_char;
+		  k++;
+		}
+	    }
+
+	  offset += 16;
+	}
+    }
+
+  stopchars[k] = '\0';
+}
+
+static const char *
+cache_glob_lookup_file_name (const char *file_name)
+{
+  const char *mime_type;
+  const char *ptr;
+  char stopchars[128];
+
+  assert (file_name != NULL);
+
+  /* First, check the literals */
+  mime_type = cache_glob_lookup_literal (file_name);
+  if (mime_type)
+    return mime_type;
+
+  find_stopchars (stopchars);
+
+  /* Next, check suffixes */
+  ptr = strpbrk (file_name, stopchars);
+  while (ptr)
+    {
+      mime_type = cache_glob_lookup_suffix (ptr, FALSE);
+      if (mime_type != NULL)
+	return mime_type;
+      
+      mime_type = cache_glob_lookup_suffix (ptr, TRUE);
+      if (mime_type != NULL)
+	return mime_type;
+
+      ptr = strpbrk (ptr + 1, stopchars);
+    }
+  
+  /* Last, try fnmatch */
+  return cache_glob_lookup_fnmatch (file_name);
+}
+
+int
+_xdg_mime_cache_get_max_buffer_extents (void)
+{
+  xdg_uint32_t offset;
+  xdg_uint32_t max_extent;
+  int i;
+
+  max_extent = 0;
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+
+      offset = GET_UINT32 (cache->buffer, 24);
+      max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
+    }
+
+  return max_extent;
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_for_data (const void *data,
+					size_t      len)
+{
+  const char *mime_type;
+  int i, priority;
+
+  priority = 0;
+  mime_type = NULL;
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+
+      int prio;
+      const char *match;
+
+      match = cache_magic_lookup_data (cache, data, len, &prio);
+      if (prio > priority)
+	{
+	  priority = prio;
+	  mime_type = match;
+	}
+    }
+
+  if (priority > 0)
+    return mime_type;
+
+  return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_for_file (const char *file_name)
+{
+  const char *mime_type;
+  FILE *file;
+  unsigned char *data;
+  int max_extent;
+  int bytes_read;
+  struct stat statbuf;
+  const char *base_name;
+
+  if (file_name == NULL)
+    return NULL;
+
+  if (! _xdg_utf8_validate (file_name))
+    return NULL;
+
+  base_name = _xdg_get_base_name (file_name);
+  mime_type = _xdg_mime_cache_get_mime_type_from_file_name (base_name);
+
+  if (mime_type != XDG_MIME_TYPE_UNKNOWN)
+    return mime_type;
+
+  if (stat (file_name, &statbuf) != 0)
+    return XDG_MIME_TYPE_UNKNOWN;
+
+  if (!S_ISREG (statbuf.st_mode))
+    return XDG_MIME_TYPE_UNKNOWN;
+
+  /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
+   * be large and need getting from a stream instead of just reading it all
+   * in. */
+  max_extent = _xdg_mime_cache_get_max_buffer_extents ();
+  data = malloc (max_extent);
+  if (data == NULL)
+    return XDG_MIME_TYPE_UNKNOWN;
+        
+  file = fopen (file_name, "r");
+  if (file == NULL)
+    {
+      free (data);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  bytes_read = fread (data, 1, max_extent, file);
+  if (ferror (file))
+    {
+      free (data);
+      fclose (file);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  mime_type = _xdg_mime_cache_get_mime_type_for_data (data, bytes_read);
+
+  free (data);
+  fclose (file);
+
+  return mime_type;
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
+{
+  const char *mime_type;
+
+  mime_type = cache_glob_lookup_file_name (file_name);
+
+  if (mime_type)
+    return mime_type;
+  else
+    return XDG_MIME_TYPE_UNKNOWN;
+}
+
+#if 1
+static int
+is_super_type (const char *mime)
+{
+  int length;
+  const char *type;
+
+  length = strlen (mime);
+  type = &(mime[length - 2]);
+
+  if (strcmp (type, "/*") == 0)
+    return 1;
+
+  return 0;
+}
+#endif
+
+int
+_xdg_mime_cache_mime_type_subclass (const char *mime,
+				    const char *base)
+{
+  const char *umime, *ubase;
+
+  int i, j, min, max, med, cmp;
+  
+  umime = _xdg_mime_cache_unalias_mime_type (mime);
+  ubase = _xdg_mime_cache_unalias_mime_type (base);
+
+  if (strcmp (umime, ubase) == 0)
+    return 1;
+
+  /* We really want to handle text/ * in GtkFileFilter, so we just
+   * turn on the supertype matching
+   */
+#if 1
+  /* Handle supertypes */
+  if (is_super_type (ubase) &&
+      xdg_mime_media_type_equal (umime, ubase))
+    return 1;
+#endif
+
+  /*  Handle special cases text/plain and application/octet-stream */
+  if (strcmp (ubase, "text/plain") == 0 && 
+      strncmp (umime, "text/", 5) == 0)
+    return 1;
+
+  if (strcmp (ubase, "application/octet-stream") == 0)
+    return 1;
+  
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+      
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset, n_parents, parent_offset;
+
+      min = 0; 
+      max = n_entries - 1;
+      while (max >= min)
+	{
+	  med = (min + max)/2;
+	  
+	  offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
+	  cmp = strcmp (cache->buffer + offset, umime);
+	  if (cmp < 0)
+	    min = med + 1;
+	  else if (cmp > 0)
+	    max = med - 1;
+	  else
+	    {
+	      offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
+	      n_parents = GET_UINT32 (cache->buffer, offset);
+	      
+	      for (j = 0; j < n_parents; j++)
+		{
+		  parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
+		  if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
+		    return 1;
+		}
+
+	      break;
+	    }
+	}
+    }
+
+  return 0;
+}
+
+const char *
+_xdg_mime_cache_unalias_mime_type (const char *mime)
+{
+  const char *lookup;
+  
+  lookup = cache_alias_lookup (mime);
+  
+  if (lookup)
+    return lookup;
+  
+  return mime;  
+}
+
+char **
+_xdg_mime_cache_list_mime_parents (const char *mime)
+{
+  int i, j, p;
+  char *all_parents[128]; /* we'll stop at 128 */ 
+  char **result;
+
+  p = 0;
+  for (i = 0; i < n_caches; i++)
+    {
+      XdgMimeCache *cache = caches[i];
+  
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+
+      for (j = 0; j < n_entries; j++)
+	{
+	  xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * i);
+	  xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * i + 4);
+	  
+	  if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
+	    {
+	      xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
+	      
+	      for (j = 0; j < n_parents; j++)
+		all_parents[p++] = cache->buffer + parents_offset + 4 + 4 * j;
+
+	      break;
+	    }
+	}
+    }
+  all_parents[p++] = 0;
+  
+  result = (char **) malloc (p * sizeof (char *));
+  memcpy (result, all_parents, p * sizeof (char *));
+
+  return result;
+}
+
diff --git a/thunar-vfs/xdgmime/xdgmimecache.h b/thunar-vfs/xdgmime/xdgmimecache.h
new file mode 100644
index 0000000000000000000000000000000000000000..98b289b3e3d37293bf2ea084e03204e86a4a5c04
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimecache.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimecache.h: Private file.  Datastructure for mmapped caches.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2005  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_CACHE_H__
+#define __XDG_MIME_CACHE_H__
+
+#include "xdgmime.h"
+
+typedef struct _XdgMimeCache XdgMimeCache;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_cache_new_from_file        XDG_ENTRY(cache_new_from_file)
+#define _xdg_mime_cache_ref                  XDG_ENTRY(cache_ref)
+#define _xdg_mime_cache_unref                XDG_ENTRY(cache_unref)
+#endif
+
+XdgMimeCache *_xdg_mime_cache_new_from_file (const char   *file_name);
+XdgMimeCache *_xdg_mime_cache_ref           (XdgMimeCache *cache);
+void          _xdg_mime_cache_unref         (XdgMimeCache *cache);
+
+
+const char  *_xdg_mime_cache_get_mime_type_for_data       (const void *data,
+		 				           size_t      len);
+const char  *_xdg_mime_cache_get_mime_type_for_file       (const char *file_name);
+const char  *_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name);
+int          _xdg_mime_cache_is_valid_mime_type           (const char *mime_type);
+int          _xdg_mime_cache_mime_type_equal              (const char *mime_a,
+						           const char *mime_b);
+int          _xdg_mime_cache_media_type_equal             (const char *mime_a,
+							   const char *mime_b);
+int          _xdg_mime_cache_mime_type_subclass           (const char *mime_a,
+							   const char *mime_b);
+char       **_xdg_mime_cache_list_mime_parents		  (const char *mime);
+const char  *_xdg_mime_cache_unalias_mime_type            (const char *mime);
+int          _xdg_mime_cache_get_max_buffer_extents       (void);
+
+#endif /* __XDG_MIME_CACHE_H__ */
diff --git a/thunar-vfs/xdgmime/xdgmimeglob.c b/thunar-vfs/xdgmime/xdgmimeglob.c
new file mode 100644
index 0000000000000000000000000000000000000000..d1f3f1940c4117847c4ddf0ee0c6695a975af5f6
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimeglob.c
@@ -0,0 +1,485 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeglob.c: Private file.  Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 "xdgmimeglob.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef	FALSE
+#define	FALSE	(0)
+#endif
+
+#ifndef	TRUE
+#define	TRUE	(!FALSE)
+#endif
+
+typedef struct XdgGlobHashNode XdgGlobHashNode;
+typedef struct XdgGlobList XdgGlobList;
+
+struct XdgGlobHashNode
+{
+  xdg_unichar_t character;
+  const char *mime_type;
+  XdgGlobHashNode *next;
+  XdgGlobHashNode *child;
+};
+struct XdgGlobList
+{
+  const char *data;
+  const char *mime_type;
+  XdgGlobList *next;
+};
+
+struct XdgGlobHash
+{
+  XdgGlobList *literal_list;
+  XdgGlobHashNode *simple_node;
+  XdgGlobList *full_list;
+};
+
+
+/* XdgGlobList
+ */
+static XdgGlobList *
+_xdg_glob_list_new (void)
+{
+  XdgGlobList *new_element;
+
+  new_element = calloc (1, sizeof (XdgGlobList));
+
+  return new_element;
+}
+
+/* Frees glob_list and all of it's children */
+static void
+_xdg_glob_list_free (XdgGlobList *glob_list)
+{
+  XdgGlobList *ptr, *next;
+
+  ptr = glob_list;
+
+  while (ptr != NULL)
+    {
+      next = ptr->next;
+
+      if (ptr->data)
+	free ((void *) ptr->data);
+      if (ptr->mime_type)
+	free ((void *) ptr->mime_type);
+      free (ptr);
+
+      ptr = next;
+    }
+}
+
+static XdgGlobList *
+_xdg_glob_list_append (XdgGlobList *glob_list,
+		       void        *data,
+		       const char  *mime_type)
+{
+  XdgGlobList *new_element;
+  XdgGlobList *tmp_element;
+
+  new_element = _xdg_glob_list_new ();
+  new_element->data = data;
+  new_element->mime_type = mime_type;
+  if (glob_list == NULL)
+    return new_element;
+
+  tmp_element = glob_list;
+  while (tmp_element->next != NULL)
+    tmp_element = tmp_element->next;
+
+  tmp_element->next = new_element;
+
+  return glob_list;
+}
+
+#if 0
+static XdgGlobList *
+_xdg_glob_list_prepend (XdgGlobList *glob_list,
+			void        *data,
+			const char  *mime_type)
+{
+  XdgGlobList *new_element;
+
+  new_element = _xdg_glob_list_new ();
+  new_element->data = data;
+  new_element->next = glob_list;
+  new_element->mime_type = mime_type;
+
+  return new_element;
+}
+#endif
+
+/* XdgGlobHashNode
+ */
+
+static XdgGlobHashNode *
+_xdg_glob_hash_node_new (void)
+{
+  XdgGlobHashNode *glob_hash_node;
+
+  glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
+
+  return glob_hash_node;
+}
+
+static void
+_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
+			  int depth)
+{
+  int i;
+  for (i = 0; i < depth; i++)
+    printf (" ");
+
+  printf ("%c", (char)glob_hash_node->character);
+  if (glob_hash_node->mime_type)
+    printf (" - %s\n", glob_hash_node->mime_type);
+  else
+    printf ("\n");
+  if (glob_hash_node->child)
+    _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
+  if (glob_hash_node->next)
+    _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
+}
+
+static XdgGlobHashNode *
+_xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
+			    const char      *text,
+			    const char      *mime_type)
+{
+  XdgGlobHashNode *node;
+  xdg_unichar_t character;
+
+  character = _xdg_utf8_to_ucs4 (text);
+
+  if ((glob_hash_node == NULL) ||
+      (character < glob_hash_node->character))
+    {
+      node = _xdg_glob_hash_node_new ();
+      node->character = character;
+      node->next = glob_hash_node;
+      glob_hash_node = node;
+    }
+  else if (character == glob_hash_node->character)
+    {
+      node = glob_hash_node;
+    }
+  else
+    {
+      XdgGlobHashNode *prev_node;
+      int found_node = FALSE;
+
+      /* Look for the first character of text in glob_hash_node, and insert it if we
+       * have to.*/
+      prev_node = glob_hash_node;
+      node = prev_node->next;
+
+      while (node != NULL)
+	{
+	  if (character < node->character)
+	    {
+	      node = _xdg_glob_hash_node_new ();
+	      node->character = character;
+	      node->next = prev_node->next;
+	      prev_node->next = node;
+
+	      found_node = TRUE;
+	      break;
+	    }
+	  else if (character == node->character)
+	    {
+	      found_node = TRUE;
+	      break;
+	    }
+	  prev_node = node;
+	  node = node->next;
+	}
+
+      if (! found_node)
+	{
+	  node = _xdg_glob_hash_node_new ();
+	  node->character = character;
+	  node->next = prev_node->next;
+	  prev_node->next = node;
+	}
+    }
+
+  text = _xdg_utf8_next_char (text);
+  if (*text == '\000')
+    {
+      free ((void *) node->mime_type);
+      node->mime_type = mime_type;
+    }
+  else
+    {
+      node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
+    }
+  return glob_hash_node;
+}
+
+static const char *
+_xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
+				      const char      *file_name,
+				      int              ignore_case)
+{
+  XdgGlobHashNode *node;
+  xdg_unichar_t character;
+
+  if (glob_hash_node == NULL)
+    return NULL;
+
+  character = _xdg_utf8_to_ucs4 (file_name);
+  if (ignore_case)
+    character = _xdg_ucs4_to_lower(character);
+
+  for (node = glob_hash_node; node && character >= node->character; node = node->next)
+    {
+      if (character == node->character)
+	{
+	  file_name = _xdg_utf8_next_char (file_name);
+	  if (*file_name == '\000')
+	    return node->mime_type;
+	  else
+	    return _xdg_glob_hash_node_lookup_file_name (node->child,
+							 file_name,
+							 ignore_case);
+	}
+    }
+  return NULL;
+}
+
+const char *
+_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+				 const char  *file_name)
+{
+  XdgGlobList *list;
+  const char *mime_type;
+  const char *ptr;
+  char stopchars[128];
+  int i;
+  XdgGlobHashNode *node;
+
+  /* First, check the literals */
+
+  assert (file_name != NULL);
+
+  for (list = glob_hash->literal_list; list; list = list->next)
+    if (strcmp ((const char *)list->data, file_name) == 0)
+      return list->mime_type;
+
+  i = 0;
+  for (node = glob_hash->simple_node; node; node = node->next)
+    {
+      if (node->character < 128)
+ 	stopchars[i++] = (char)node->character;
+    }
+  stopchars[i] = '\0';
+ 
+  ptr = strpbrk (file_name, stopchars);
+  while (ptr)
+    {
+      mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE));
+      if (mime_type != NULL)
+        return mime_type;
+      
+      mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE));
+      if (mime_type != NULL)
+        return mime_type;
+      
+      ptr = strpbrk (ptr + 1, stopchars);
+    }
+
+  /* FIXME: Not UTF-8 safe */
+  for (list = glob_hash->full_list; list; list = list->next)
+    if (fnmatch ((const char *)list->data, file_name, 0) == 0)
+      return list->mime_type;
+
+  return NULL;
+}
+
+
+
+/* XdgGlobHash
+ */
+
+XdgGlobHash *
+_xdg_glob_hash_new (void)
+{
+  XdgGlobHash *glob_hash;
+
+  glob_hash = calloc (1, sizeof (XdgGlobHash));
+
+  return glob_hash;
+}
+
+
+static void
+_xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
+{
+  if (node)
+    {
+      if (node->child)
+       _xdg_glob_hash_free_nodes (node->child);
+      if (node->next)
+       _xdg_glob_hash_free_nodes (node->next);
+      if (node->mime_type)
+	free ((void *) node->mime_type);
+      free (node);
+    }
+}
+
+void
+_xdg_glob_hash_free (XdgGlobHash *glob_hash)
+{
+  _xdg_glob_list_free (glob_hash->literal_list);
+  _xdg_glob_list_free (glob_hash->full_list);
+  _xdg_glob_hash_free_nodes (glob_hash->simple_node);
+  free (glob_hash);
+}
+
+XdgGlobType
+_xdg_glob_determine_type (const char *glob)
+{
+  const char *ptr;
+  int maybe_in_simple_glob = FALSE;
+  int first_char = TRUE;
+
+  ptr = glob;
+
+  while (*ptr != '\000')
+    {
+      if (*ptr == '*' && first_char)
+	maybe_in_simple_glob = TRUE;
+      else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
+	  return XDG_GLOB_FULL;
+
+      first_char = FALSE;
+      ptr = _xdg_utf8_next_char (ptr);
+    }
+  if (maybe_in_simple_glob)
+    return XDG_GLOB_SIMPLE;
+  else
+    return XDG_GLOB_LITERAL;
+}
+
+/* glob must be valid UTF-8 */
+void
+_xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
+			    const char  *glob,
+			    const char  *mime_type)
+{
+  XdgGlobType type;
+
+  assert (glob_hash != NULL);
+  assert (glob != NULL);
+
+  type = _xdg_glob_determine_type (glob);
+
+  switch (type)
+    {
+    case XDG_GLOB_LITERAL:
+      glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
+      break;
+    case XDG_GLOB_SIMPLE:
+      glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, strdup (mime_type));
+      break;
+    case XDG_GLOB_FULL:
+      glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
+      break;
+    }
+}
+
+void
+_xdg_glob_hash_dump (XdgGlobHash *glob_hash)
+{
+  XdgGlobList *list;
+  printf ("LITERAL STRINGS\n");
+  if (glob_hash->literal_list == NULL)
+    {
+      printf ("    None\n");
+    }
+  else
+    {
+      for (list = glob_hash->literal_list; list; list = list->next)
+	printf ("    %s - %s\n", (char *)list->data, list->mime_type);
+    }
+  printf ("\nSIMPLE GLOBS\n");
+  _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
+
+  printf ("\nFULL GLOBS\n");
+  if (glob_hash->full_list == NULL)
+    {
+      printf ("    None\n");
+    }
+  else
+    {
+      for (list = glob_hash->full_list; list; list = list->next)
+	printf ("    %s - %s\n", (char *)list->data, list->mime_type);
+    }
+}
+
+
+void
+_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
+			       const char  *file_name)
+{
+  FILE *glob_file;
+  char line[255];
+
+  glob_file = fopen (file_name, "r");
+
+  if (glob_file == NULL)
+    return;
+
+  /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
+   * Blah */
+  while (fgets (line, 255, glob_file) != NULL)
+    {
+      char *colon;
+      if (line[0] == '#')
+	continue;
+
+      colon = strchr (line, ':');
+      if (colon == NULL)
+	continue;
+      *(colon++) = '\000';
+      colon[strlen (colon) -1] = '\000';
+      _xdg_glob_hash_append_glob (glob_hash, colon, line);
+    }
+
+  fclose (glob_file);
+}
diff --git a/thunar-vfs/xdgmime/xdgmimeglob.h b/thunar-vfs/xdgmime/xdgmimeglob.h
new file mode 100644
index 0000000000000000000000000000000000000000..771c4527f3bc65c41ffa313d1f53fa5321bbe11b
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimeglob.h
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeglob.h: Private file.  Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_GLOB_H__
+#define __XDG_MIME_GLOB_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgGlobHash XdgGlobHash;
+
+typedef enum
+{
+  XDG_GLOB_LITERAL, /* Makefile */
+  XDG_GLOB_SIMPLE,  /* *.gif */
+  XDG_GLOB_FULL     /* x*.[ch] */
+} XdgGlobType;
+
+  
+#ifdef XDG_PREFIX
+#define _xdg_mime_glob_read_from_file         XDG_ENTRY(glob_read_from_file)
+#define _xdg_glob_hash_new                    XDG_ENTRY(hash_new)
+#define _xdg_glob_hash_free                   XDG_ENTRY(hash_free)
+#define _xdg_glob_hash_lookup_file_name       XDG_ENTRY(hash_lookup_file_name)
+#define _xdg_glob_hash_append_glob            XDG_ENTRY(hash_append_glob)
+#define _xdg_glob_determine_type              XDG_ENTRY(determine_type)
+#define _xdg_glob_hash_dump                   XDG_ENTRY(hash_dump)
+#endif
+
+void         _xdg_mime_glob_read_from_file   (XdgGlobHash *glob_hash,
+					      const char  *file_name);
+XdgGlobHash *_xdg_glob_hash_new              (void);
+void         _xdg_glob_hash_free             (XdgGlobHash *glob_hash);
+const char  *_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+					      const char  *text);
+void         _xdg_glob_hash_append_glob      (XdgGlobHash *glob_hash,
+					      const char  *glob,
+					      const char  *mime_type);
+XdgGlobType  _xdg_glob_determine_type        (const char  *glob);
+void         _xdg_glob_hash_dump             (XdgGlobHash *glob_hash);
+
+#endif /* __XDG_MIME_GLOB_H__ */
diff --git a/thunar-vfs/xdgmime/xdgmimeint.c b/thunar-vfs/xdgmime/xdgmimeint.c
new file mode 100644
index 0000000000000000000000000000000000000000..4a0ac4cc397e1d8b6f1c357b9731d4b46445a719
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimeint.c
@@ -0,0 +1,154 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeint.c: Internal defines and functions.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 "xdgmimeint.h"
+#include <ctype.h>
+#include <string.h>
+
+#ifndef	FALSE
+#define	FALSE	(0)
+#endif
+
+#ifndef	TRUE
+#define	TRUE	(!FALSE)
+#endif
+
+static const char _xdg_utf8_skip_data[256] = {
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
+};
+
+const char * const _xdg_utf8_skip = _xdg_utf8_skip_data;
+
+
+
+/* Returns the number of unprocessed characters. */
+xdg_unichar_t
+_xdg_utf8_to_ucs4(const char *source)
+{
+  xdg_unichar_t ucs32;
+  if( ! ( *source & 0x80 ) )
+    {
+      ucs32 = *source;
+    }
+  else
+    {
+      int bytelength = 0;
+      xdg_unichar_t result;
+      if ( ! (*source & 0x40) )
+	{
+	  ucs32 = *source;
+	}
+      else
+	{
+	  if ( ! (*source & 0x20) )
+	    {
+	      result = *source++ & 0x1F;
+	      bytelength = 2;
+	    }
+	  else if ( ! (*source & 0x10) )
+	    {
+	      result = *source++ & 0x0F;
+	      bytelength = 3;
+	    }
+	  else if ( ! (*source & 0x08) )
+	    {
+	      result = *source++ & 0x07;
+	      bytelength = 4;
+	    }
+	  else if ( ! (*source & 0x04) )
+	    {
+	      result = *source++ & 0x03;
+	      bytelength = 5;
+	    }
+	  else if ( ! (*source & 0x02) )
+	    {
+	      result = *source++ & 0x01;
+	      bytelength = 6;
+	    }
+	  else
+	    {
+	      result = *source++;
+	      bytelength = 1;
+	    }
+
+	  for ( bytelength --; bytelength > 0; bytelength -- )
+	    {
+	      result <<= 6;
+	      result |= *source++ & 0x3F;
+	    }
+	  ucs32 = result;
+	}
+    }
+  return ucs32;
+}
+
+
+/* hullo.  this is great code.  don't rewrite it */
+
+xdg_unichar_t
+_xdg_ucs4_to_lower (xdg_unichar_t source)
+{
+  /* FIXME: Do a real to_upper sometime */
+  /* CaseFolding-3.2.0.txt has a table of rules. */
+  if ((source & 0xFF) == source)
+    return (xdg_unichar_t) tolower ((unsigned char) source);
+  return source;
+}
+
+int
+_xdg_utf8_validate (const char *source)
+{
+  /* FIXME: actually write */
+  return TRUE;
+}
+
+const char *
+_xdg_get_base_name (const char *file_name)
+{
+  const char *base_name;
+
+  if (file_name == NULL)
+    return NULL;
+
+  base_name = strrchr (file_name, '/');
+
+  if (base_name == NULL)
+    return file_name;
+  else
+    return base_name + 1;
+}
diff --git a/thunar-vfs/xdgmime/xdgmimeint.h b/thunar-vfs/xdgmime/xdgmimeint.h
new file mode 100644
index 0000000000000000000000000000000000000000..288148719e4b0f90cee6a0ea36b8c6f785303a0b
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimeint.h
@@ -0,0 +1,73 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeint.h: Internal defines and functions.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_INT_H__
+#define __XDG_MIME_INT_H__
+
+#include "xdgmime.h"
+
+
+#ifndef	FALSE
+#define	FALSE (0)
+#endif
+
+#ifndef	TRUE
+#define	TRUE (!FALSE)
+#endif
+
+/* FIXME: Needs to be configure check */
+typedef unsigned int   xdg_unichar_t;
+typedef unsigned char  xdg_uchar8_t;
+typedef unsigned short xdg_uint16_t;
+typedef unsigned int   xdg_uint32_t;
+
+#ifdef XDG_PREFIX
+#define _xdg_utf8_skip   XDG_ENTRY(utf8_skip)
+#define _xdg_utf8_to_ucs4   XDG_ENTRY(utf8_to_ucs4)
+#define _xdg_ucs4_to_lower   XDG_ENTRY(ucs4_to_lower)
+#define _xdg_utf8_validate   XDG_ENTRY(utf8_validate)
+#define _xdg_get_base_name   XDG_ENTRY(get_ase_name)
+#endif
+
+#define SWAP_BE16_TO_LE16(val) (xdg_uint16_t)(((xdg_uint16_t)(val) << 8)|((xdg_uint16_t)(val) >> 8))
+
+#define SWAP_BE32_TO_LE32(val) (xdg_uint32_t)((((xdg_uint32_t)(val) & 0xFF000000U) >> 24) |	\
+					      (((xdg_uint32_t)(val) & 0x00FF0000U) >> 8) |	\
+					      (((xdg_uint32_t)(val) & 0x0000FF00U) << 8) |	\
+					      (((xdg_uint32_t)(val) & 0x000000FFU) << 24))
+/* UTF-8 utils
+ */
+extern const char *const _xdg_utf8_skip;
+#define _xdg_utf8_next_char(p) (char *)((p) + _xdg_utf8_skip[*(unsigned char *)(p)])
+#define _xdg_utf8_char_size(p) (int) (_xdg_utf8_skip[*(unsigned char *)(p)])
+
+xdg_unichar_t  _xdg_utf8_to_ucs4  (const char    *source);
+xdg_unichar_t  _xdg_ucs4_to_lower (xdg_unichar_t  source);
+int            _xdg_utf8_validate (const char    *source);
+const char    *_xdg_get_base_name (const char    *file_name);
+
+#endif /* __XDG_MIME_INT_H__ */
diff --git a/thunar-vfs/xdgmime/xdgmimemagic.c b/thunar-vfs/xdgmime/xdgmimemagic.c
new file mode 100644
index 0000000000000000000000000000000000000000..352886d0f626c0faa3b0cf05be2e37849cf9be8f
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimemagic.c
@@ -0,0 +1,784 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.: Private file.  Datastructure for storing magic files.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <assert.h>
+#include "xdgmimemagic.h"
+#include "xdgmimeint.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef	FALSE
+#define	FALSE	(0)
+#endif
+
+#ifndef	TRUE
+#define	TRUE	(!FALSE)
+#endif
+
+extern int errno;
+
+typedef struct XdgMimeMagicMatch XdgMimeMagicMatch;
+typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet;
+
+typedef enum
+{
+  XDG_MIME_MAGIC_SECTION,
+  XDG_MIME_MAGIC_MAGIC,
+  XDG_MIME_MAGIC_ERROR,
+  XDG_MIME_MAGIC_EOF
+} XdgMimeMagicState;
+
+struct XdgMimeMagicMatch
+{
+  const char *mime_type;
+  int priority;
+  XdgMimeMagicMatchlet *matchlet;
+  XdgMimeMagicMatch *next;
+};
+
+
+struct XdgMimeMagicMatchlet
+{
+  int indent;
+  int offset;
+  unsigned int value_length;
+  unsigned char *value;
+  unsigned char *mask;
+  unsigned int range_length;
+  unsigned int word_size;
+  XdgMimeMagicMatchlet *next;
+};
+
+
+struct XdgMimeMagic
+{
+  XdgMimeMagicMatch *match_list;
+  int max_extent;
+};
+
+static XdgMimeMagicMatch *
+_xdg_mime_magic_match_new (void)
+{
+  return calloc (1, sizeof (XdgMimeMagicMatch));
+}
+
+
+static XdgMimeMagicMatchlet *
+_xdg_mime_magic_matchlet_new (void)
+{
+  XdgMimeMagicMatchlet *matchlet;
+
+  matchlet = malloc (sizeof (XdgMimeMagicMatchlet));
+
+  matchlet->indent = 0;
+  matchlet->offset = 0;
+  matchlet->value_length = 0;
+  matchlet->value = NULL;
+  matchlet->mask = NULL;
+  matchlet->range_length = 1;
+  matchlet->word_size = 1;
+  matchlet->next = NULL;
+
+  return matchlet;
+}
+
+
+static void
+_xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet)
+{
+  if (mime_magic_matchlet)
+    {
+      if (mime_magic_matchlet->next)
+	_xdg_mime_magic_matchlet_free (mime_magic_matchlet->next);
+      if (mime_magic_matchlet->value)
+	free (mime_magic_matchlet->value);
+      if (mime_magic_matchlet->mask)
+	free (mime_magic_matchlet->mask);
+      free (mime_magic_matchlet);
+    }
+}
+
+
+/* Frees mime_magic_match and the remainder of its list
+ */
+static void
+_xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match)
+{
+  XdgMimeMagicMatch *ptr, *next;
+
+  ptr = mime_magic_match;
+  while (ptr)
+    {
+      next = ptr->next;
+
+      if (ptr->mime_type)
+	free ((void *) ptr->mime_type);
+      if (ptr->matchlet)
+	_xdg_mime_magic_matchlet_free (ptr->matchlet);
+      free (ptr);
+
+      ptr = next;
+    }
+}
+
+/* Reads in a hunk of data until a newline character or a '\000' is hit.  The
+ * returned string is null terminated, and doesn't include the newline.
+ */
+static unsigned char *
+_xdg_mime_magic_read_to_newline (FILE *magic_file,
+				 int  *end_of_file)
+{
+  unsigned char *retval;
+  int c;
+  int len, pos;
+
+  len = 128;
+  pos = 0;
+  retval = malloc (len);
+  *end_of_file = FALSE;
+
+  while (TRUE)
+    {
+      c = getc_unlocked (magic_file);
+      if (c == EOF)
+	{
+	  *end_of_file = TRUE;
+	  break;
+	}
+      if (c == '\n' || c == '\000')
+	break;
+      retval[pos++] = (unsigned char) c;
+      if (pos % 128 == 127)
+	{
+	  len = len + 128;
+	  retval = realloc (retval, len);
+	}
+    }
+
+  retval[pos] = '\000';
+  return retval;
+}
+
+/* Returns the number read from the file, or -1 if no number could be read.
+ */
+static int
+_xdg_mime_magic_read_a_number (FILE *magic_file,
+			       int  *end_of_file)
+{
+  /* LONG_MAX is about 20 characters on my system */
+#define MAX_NUMBER_SIZE 30
+  char number_string[MAX_NUMBER_SIZE + 1];
+  int pos = 0;
+  int c;
+  long retval = -1;
+
+  while (TRUE)
+    {
+      c = getc_unlocked (magic_file);
+
+      if (c == EOF)
+	{
+	  *end_of_file = TRUE;
+	  break;
+	}
+      if (! isdigit (c))
+	{
+	  ungetc (c, magic_file);
+	  break;
+	}
+      number_string[pos] = (char) c;
+      pos++;
+      if (pos == MAX_NUMBER_SIZE)
+	break;
+    }
+  if (pos > 0)
+    {
+      number_string[pos] = '\000';
+      errno = 0;
+      retval = strtol (number_string, NULL, 10);
+
+      if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0))
+	return -1;
+    }
+
+  return retval;
+}
+
+/* Headers are of the format:
+ * [<priority>:<mime-type>]
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match)
+{
+  int c;
+  char *buffer;
+  char *end_ptr;
+  int end_of_file = 0;
+
+  assert (magic_file != NULL);
+  assert (match != NULL);
+
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+  if (c != '[')
+    return XDG_MIME_MAGIC_ERROR;
+
+  match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+  if (end_of_file)
+    return XDG_MIME_MAGIC_EOF;
+  if (match->priority == -1)
+    return XDG_MIME_MAGIC_ERROR;
+
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+  if (c != ':')
+    return XDG_MIME_MAGIC_ERROR;
+
+  buffer = (char *)_xdg_mime_magic_read_to_newline (magic_file, &end_of_file);
+  if (end_of_file)
+    return XDG_MIME_MAGIC_EOF;
+
+  end_ptr = buffer;
+  while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n')
+    end_ptr++;
+  if (*end_ptr != ']')
+    {
+      free (buffer);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+  *end_ptr = '\000';
+
+  match->mime_type = strdup (buffer);
+  free (buffer);
+
+  return XDG_MIME_MAGIC_MAGIC;
+}
+
+static XdgMimeMagicState
+_xdg_mime_magic_parse_error (FILE *magic_file)
+{
+  int c;
+
+  while (1)
+    {
+      c = getc_unlocked (magic_file);
+      if (c == EOF)
+	return XDG_MIME_MAGIC_EOF;
+      if (c == '\n')
+	return XDG_MIME_MAGIC_SECTION;
+    }
+}
+
+/* Headers are of the format:
+ * [ indent ] ">" start-offset "=" value
+ * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_magic_line (FILE              *magic_file,
+				  XdgMimeMagicMatch *match)
+{
+  XdgMimeMagicMatchlet *matchlet;
+  int c;
+  int end_of_file;
+  int indent = 0;
+  int bytes_read;
+
+  assert (magic_file != NULL);
+
+  /* Sniff the buffer to make sure it's a valid line */
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+  else if (c == '[')
+    {
+      ungetc (c, magic_file);
+      return XDG_MIME_MAGIC_SECTION;
+    }
+  else if (c == '\n')
+    return XDG_MIME_MAGIC_MAGIC;
+
+  /* At this point, it must be a digit or a '>' */
+  end_of_file = FALSE;
+  if (isdigit (c))
+    {
+      ungetc (c, magic_file);
+      indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+      if (end_of_file)
+	return XDG_MIME_MAGIC_EOF;
+      if (indent == -1)
+	return XDG_MIME_MAGIC_ERROR;
+      c = getc_unlocked (magic_file);
+      if (c == EOF)
+	return XDG_MIME_MAGIC_EOF;
+    }
+
+  if (c != '>')
+    return XDG_MIME_MAGIC_ERROR;
+
+  matchlet = _xdg_mime_magic_matchlet_new ();
+  matchlet->indent = indent;
+  matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+  if (end_of_file)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  if (matchlet->offset == -1)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  else if (c != '=')
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+
+  /* Next two bytes determine how long the value is */
+  matchlet->value_length = 0;
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  matchlet->value_length = c & 0xFF;
+  matchlet->value_length = matchlet->value_length << 8;
+
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  matchlet->value_length = matchlet->value_length + (c & 0xFF);
+
+  matchlet->value = malloc (matchlet->value_length);
+
+  /* OOM */
+  if (matchlet->value == NULL)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+  bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file);
+  if (bytes_read != matchlet->value_length)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      if (feof (magic_file))
+	return XDG_MIME_MAGIC_EOF;
+      else
+	return XDG_MIME_MAGIC_ERROR;
+    }
+
+  c = getc_unlocked (magic_file);
+  if (c == '&')
+    {
+      matchlet->mask = malloc (matchlet->value_length);
+      /* OOM */
+      if (matchlet->mask == NULL)
+	{
+	  _xdg_mime_magic_matchlet_free (matchlet);
+	  return XDG_MIME_MAGIC_ERROR;
+	}
+      bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file);
+      if (bytes_read != matchlet->value_length)
+	{
+	  _xdg_mime_magic_matchlet_free (matchlet);
+	  if (feof (magic_file))
+	    return XDG_MIME_MAGIC_EOF;
+	  else
+	    return XDG_MIME_MAGIC_ERROR;
+	}
+      c = getc_unlocked (magic_file);
+    }
+
+  if (c == '~')
+    {
+      matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+      if (end_of_file)
+	{
+	  _xdg_mime_magic_matchlet_free (matchlet);
+	  return XDG_MIME_MAGIC_EOF;
+	}
+      if (matchlet->word_size != 0 &&
+	  matchlet->word_size != 1 &&
+	  matchlet->word_size != 2 &&
+	  matchlet->word_size != 4)
+	{
+	  _xdg_mime_magic_matchlet_free (matchlet);
+	  return XDG_MIME_MAGIC_ERROR;
+	}
+      c = getc_unlocked (magic_file);
+    }
+
+  if (c == '+')
+    {
+      matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+      if (end_of_file)
+	{
+	  _xdg_mime_magic_matchlet_free (matchlet);
+	  return XDG_MIME_MAGIC_EOF;
+	}
+      if (matchlet->range_length == -1)
+	{
+	  _xdg_mime_magic_matchlet_free (matchlet);
+	  return XDG_MIME_MAGIC_ERROR;
+	}
+      c = getc_unlocked (magic_file);
+    }
+
+
+  if (c == '\n')
+    {
+      /* We clean up the matchlet, byte swapping if needed */
+      if (matchlet->word_size > 1)
+	{
+	  int i;
+	  if (matchlet->value_length % matchlet->word_size != 0)
+	    {
+	      _xdg_mime_magic_matchlet_free (matchlet);
+	      return XDG_MIME_MAGIC_ERROR;
+	    }
+	  /* FIXME: need to get this defined in a <config.h> style file */
+#if LITTLE_ENDIAN
+	  for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size)
+	    {
+	      if (matchlet->word_size == 2)
+		*((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i)));
+	      else if (matchlet->word_size == 4)
+		*((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i)));
+	      if (matchlet->mask)
+		{
+		  if (matchlet->word_size == 2)
+		    *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i)));
+		  else if (matchlet->word_size == 4)
+		    *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i)));
+
+		}
+	    }
+#endif
+	}
+
+      matchlet->next = match->matchlet;
+      match->matchlet = matchlet;
+
+
+      return XDG_MIME_MAGIC_MAGIC;
+    }
+
+  _xdg_mime_magic_matchlet_free (matchlet);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+
+  return XDG_MIME_MAGIC_ERROR;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet,
+					  const void           *data,
+					  size_t                len)
+{
+  int i, j;
+  for (i = matchlet->offset; i < matchlet->offset + matchlet->range_length; i++)
+    {
+      int valid_matchlet = TRUE;
+
+      if (i + matchlet->value_length > len)
+	return FALSE;
+
+      if (matchlet->mask)
+	{
+	  for (j = 0; j < matchlet->value_length; j++)
+	    {
+	      if ((matchlet->value[j] & matchlet->mask[j]) !=
+		  ((((unsigned char *) data)[j + i]) & matchlet->mask[j]))
+		{
+		  valid_matchlet = FALSE;
+		  break;
+		}
+	    }
+	}
+      else
+	{
+	  for (j = 0; j <  matchlet->value_length; j++)
+	    {
+	      if (matchlet->value[j] != ((unsigned char *) data)[j + i])
+		{
+		  valid_matchlet = FALSE;
+		  break;
+		}
+	    }
+	}
+      if (valid_matchlet)
+	return TRUE;
+    }
+  return FALSE;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet,
+					const void           *data,
+					size_t                len,
+					int                   indent)
+{
+  while ((matchlet != NULL) && (matchlet->indent == indent))
+    {
+      if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len))
+	{
+	  if ((matchlet->next == NULL) ||
+	      (matchlet->next->indent <= indent))
+	    return TRUE;
+
+	  if (_xdg_mime_magic_matchlet_compare_level (matchlet->next,
+						      data,
+						      len,
+						      indent + 1))
+	    return TRUE;
+	}
+
+      do
+	{
+	  matchlet = matchlet->next;
+	}
+      while (matchlet && matchlet->indent > indent);
+    }
+
+  return FALSE;
+}
+
+static int
+_xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match,
+				       const void        *data,
+				       size_t             len)
+{
+  return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0);
+}
+
+static void
+_xdg_mime_magic_insert_match (XdgMimeMagic      *mime_magic,
+			      XdgMimeMagicMatch *match)
+{
+  XdgMimeMagicMatch *list;
+
+  if (mime_magic->match_list == NULL)
+    {
+      mime_magic->match_list = match;
+      return;
+    }
+
+  if (match->priority > mime_magic->match_list->priority)
+    {
+      match->next = mime_magic->match_list;
+      mime_magic->match_list = match;
+      return;
+    }
+
+  list = mime_magic->match_list;
+  while (list->next != NULL)
+    {
+      if (list->next->priority < match->priority)
+	{
+	  match->next = list->next;
+	  list->next = match;
+	  return;
+	}
+      list = list->next;
+    }
+  list->next = match;
+  match->next = NULL;
+}
+
+XdgMimeMagic *
+_xdg_mime_magic_new (void)
+{
+  return calloc (1, sizeof (XdgMimeMagic));
+}
+
+void
+_xdg_mime_magic_free (XdgMimeMagic *mime_magic)
+{
+  if (mime_magic) {
+    _xdg_mime_magic_match_free (mime_magic->match_list);
+    free (mime_magic);
+  }
+}
+
+int
+_xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic)
+{
+  return mime_magic->max_extent;
+}
+
+const char *
+_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
+			     const void   *data,
+			     size_t        len)
+{
+  XdgMimeMagicMatch *match;
+  const char *mime_type;
+
+  mime_type = NULL;
+  for (match = mime_magic->match_list; match; match = match->next)
+    {
+      if (_xdg_mime_magic_match_compare_to_data (match, data, len))
+	{
+	  if ((mime_type == NULL) || (xdg_mime_mime_type_subclass (match->mime_type, mime_type))) {
+	    mime_type = match->mime_type;
+	  }
+	}
+    }
+
+  return mime_type;
+}
+
+static void
+_xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic)
+{
+  XdgMimeMagicMatch *match;
+  int max_extent = 0;
+
+  for (match = mime_magic->match_list; match; match = match->next)
+    {
+      XdgMimeMagicMatchlet *matchlet;
+
+      for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next)
+	{
+	  int extent;
+
+	  extent = matchlet->value_length + matchlet->offset + matchlet->range_length;
+	  if (max_extent < extent)
+	    max_extent = extent;
+	}
+    }
+
+  mime_magic->max_extent = max_extent;
+}
+
+static XdgMimeMagicMatchlet *
+_xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets)
+{
+  XdgMimeMagicMatchlet *new_list;
+  XdgMimeMagicMatchlet *tmp;
+
+  if ((matchlets == NULL) || (matchlets->next == NULL))
+    return matchlets;
+
+  new_list = NULL;
+  tmp = matchlets;
+  while (tmp != NULL)
+    {
+      XdgMimeMagicMatchlet *matchlet;
+
+      matchlet = tmp;
+      tmp = tmp->next;
+      matchlet->next = new_list;
+      new_list = matchlet;
+    }
+
+  return new_list;
+
+}
+
+static void
+_xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic,
+				 FILE         *magic_file)
+{
+  XdgMimeMagicState state;
+  XdgMimeMagicMatch *match = NULL; /* Quiet compiler */
+
+  state = XDG_MIME_MAGIC_SECTION;
+
+  while (state != XDG_MIME_MAGIC_EOF)
+    {
+      switch (state)
+	{
+	case XDG_MIME_MAGIC_SECTION:
+	  match = _xdg_mime_magic_match_new ();
+	  state = _xdg_mime_magic_parse_header (magic_file, match);
+	  if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+	    _xdg_mime_magic_match_free (match);
+	  break;
+	case XDG_MIME_MAGIC_MAGIC:
+	  state = _xdg_mime_magic_parse_magic_line (magic_file, match);
+	  if (state == XDG_MIME_MAGIC_SECTION ||
+	      (state == XDG_MIME_MAGIC_EOF && match->mime_type))
+	    {
+	      match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet);
+	      _xdg_mime_magic_insert_match (mime_magic, match);
+	    }
+	  else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+	    _xdg_mime_magic_match_free (match);
+	  break;
+	case XDG_MIME_MAGIC_ERROR:
+	  state = _xdg_mime_magic_parse_error (magic_file);
+	  break;
+	case XDG_MIME_MAGIC_EOF:
+	default:
+	  /* Make the compiler happy */
+	  assert (0);
+	}
+    }
+  _xdg_mime_update_mime_magic_extents (mime_magic);
+}
+
+void
+_xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
+				const char   *file_name)
+{
+  FILE *magic_file;
+  char header[12];
+
+  magic_file = fopen (file_name, "r");
+
+  if (magic_file == NULL)
+    return;
+
+  if (fread (header, 1, 12, magic_file) == 12)
+    {
+      if (memcmp ("MIME-Magic\0\n", header, 12) == 0)
+        _xdg_mime_magic_read_magic_file (mime_magic, magic_file);
+    }
+
+  fclose (magic_file);
+}
diff --git a/thunar-vfs/xdgmime/xdgmimemagic.h b/thunar-vfs/xdgmime/xdgmimemagic.h
new file mode 100644
index 0000000000000000000000000000000000000000..dea0a3c040589695362607831a0818f9266ebea7
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimemagic.h
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.h: Private file.  Datastructure for storing the magic files.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_MAGIC_H__
+#define __XDG_MIME_MAGIC_H__
+
+#include <unistd.h>
+#include "xdgmime.h"
+typedef struct XdgMimeMagic XdgMimeMagic;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_glob_read_from_file             XDG_ENTRY(glob_read_from_file)
+#define _xdg_mime_magic_new                       XDG_ENTRY(magic_new)
+#define _xdg_mime_magic_read_from_file            XDG_ENTRY(magic_read_from_file)
+#define _xdg_mime_magic_free                      XDG_ENTRY(magic_free)
+#define _xdg_mime_magic_get_buffer_extents        XDG_ENTRY(magic_get_buffer_extents)
+#define _xdg_mime_magic_lookup_data               XDG_ENTRY(magic_lookup_data)
+#endif
+
+
+XdgMimeMagic *_xdg_mime_magic_new                (void);
+void          _xdg_mime_magic_read_from_file     (XdgMimeMagic *mime_magic,
+						  const char   *file_name);
+void          _xdg_mime_magic_free               (XdgMimeMagic *mime_magic);
+int           _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic);
+const char   *_xdg_mime_magic_lookup_data        (XdgMimeMagic *mime_magic,
+						  const void   *data,
+						  size_t        len);
+
+#endif /* __XDG_MIME_MAGIC_H__ */
diff --git a/thunar-vfs/xdgmime/xdgmimeparent.c b/thunar-vfs/xdgmime/xdgmimeparent.c
new file mode 100644
index 0000000000000000000000000000000000000000..511bbacbc19c19b59cbcf359499aa492e3bc6cca
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimeparent.c
@@ -0,0 +1,219 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file.  Datastructure for storing the hierarchy.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 2004  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 "xdgmimeparent.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef	FALSE
+#define	FALSE	(0)
+#endif
+
+#ifndef	TRUE
+#define	TRUE	(!FALSE)
+#endif
+
+typedef struct XdgMimeParents XdgMimeParents;
+
+struct XdgMimeParents
+{
+  char *mime;
+  char **parents;
+  int n_parents;
+};
+
+struct XdgParentList
+{
+  struct XdgMimeParents *parents;
+  int n_mimes;
+};
+
+XdgParentList *
+_xdg_mime_parent_list_new (void)
+{
+  XdgParentList *list;
+
+  list = malloc (sizeof (XdgParentList));
+
+  list->parents = NULL;
+  list->n_mimes = 0;
+
+  return list;
+}
+
+void         
+_xdg_mime_parent_list_free (XdgParentList *list)
+{
+  int i;
+  char **p;
+
+  if (list->parents)
+    {
+      for (i = 0; i < list->n_mimes; i++)
+	{
+	  for (p = list->parents[i].parents; *p; p++)
+	    free (*p);
+
+	  free (list->parents[i].parents);
+	  free (list->parents[i].mime);
+	}
+      free (list->parents);
+    }
+  free (list);
+}
+
+static int
+parent_entry_cmp (const void *v1, const void *v2)
+{
+  return strcmp (((XdgMimeParents *)v1)->mime, ((XdgMimeParents *)v2)->mime);
+}
+
+const char **
+_xdg_mime_parent_list_lookup (XdgParentList *list,
+			      const char    *mime)
+{
+  XdgMimeParents *entry;
+  XdgMimeParents key;
+
+  if (list->n_mimes > 0)
+    {
+      key.mime = (char *)mime;
+      key.parents = NULL;
+
+      entry = bsearch (&key, list->parents, list->n_mimes,
+		       sizeof (XdgMimeParents), &parent_entry_cmp);
+      if (entry)
+        return (const char **)entry->parents;
+    }
+
+  return NULL;
+}
+
+void
+_xdg_mime_parent_read_from_file (XdgParentList *list,
+				 const char    *file_name)
+{
+  FILE *file;
+  char line[255];
+  int i, alloc;
+  XdgMimeParents *entry;
+
+  file = fopen (file_name, "r");
+
+  if (file == NULL)
+    return;
+
+  /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
+   * Blah */
+  alloc = list->n_mimes + 16;
+  list->parents = realloc (list->parents, alloc * sizeof (XdgMimeParents));
+  while (fgets (line, 255, file) != NULL)
+    {
+      char *sep;
+      if (line[0] == '#')
+	continue;
+
+      sep = strchr (line, ' ');
+      if (sep == NULL)
+	continue;
+      *(sep++) = '\000';
+      sep[strlen (sep) -1] = '\000';
+      entry = NULL;
+      for (i = 0; i < list->n_mimes; i++)
+	{
+	  if (strcmp (list->parents[i].mime, line) == 0)
+	    {
+	      entry = &(list->parents[i]);
+	      break;
+	    }
+	}
+      
+      if (!entry)
+	{
+	  if (list->n_mimes == alloc)
+	    {
+	      alloc <<= 1;
+	      list->parents = realloc (list->parents, 
+				       alloc * sizeof (XdgMimeParents));
+	    }
+	  list->parents[list->n_mimes].mime = strdup (line);
+	  list->parents[list->n_mimes].parents = NULL;
+	  entry = &(list->parents[list->n_mimes]);
+	  list->n_mimes++;
+	}
+
+      if (!entry->parents)
+	{
+	  entry->n_parents = 1;
+	  entry->parents = malloc ((entry->n_parents + 1) * sizeof (char *));
+	}
+      else
+	{
+	  entry->n_parents += 1;
+	  entry->parents = realloc (entry->parents, 
+				    (entry->n_parents + 2) * sizeof (char *));
+	}
+      entry->parents[entry->n_parents - 1] = strdup (sep);
+      entry->parents[entry->n_parents] = NULL;
+    }
+
+  list->parents = realloc (list->parents, 
+			   list->n_mimes * sizeof (XdgMimeParents));
+
+  fclose (file);  
+  
+  if (list->n_mimes > 1)
+    qsort (list->parents, list->n_mimes, 
+           sizeof (XdgMimeParents), &parent_entry_cmp);
+}
+
+
+void         
+_xdg_mime_parent_list_dump (XdgParentList *list)
+{
+  int i;
+  char **p;
+
+  if (list->parents)
+    {
+      for (i = 0; i < list->n_mimes; i++)
+	{
+	  for (p = list->parents[i].parents; *p; p++)
+	    printf ("%s %s\n", list->parents[i].mime, *p);
+	}
+    }
+}
+
+
diff --git a/thunar-vfs/xdgmime/xdgmimeparent.h b/thunar-vfs/xdgmime/xdgmimeparent.h
new file mode 100644
index 0000000000000000000000000000000000000000..da29452cb8ee5998126328ea13dc47f9ccd8b3eb
--- /dev/null
+++ b/thunar-vfs/xdgmime/xdgmimeparent.h
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeparent.h: Private file.  Datastructure for storing the hierarchy.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 200  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_PARENT_H__
+#define __XDG_MIME_PARENT_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgParentList XdgParentList;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_parent_read_from_file        XDG_ENTRY(parent_read_from_file)
+#define _xdg_mime_parent_list_new              XDG_ENTRY(parent_list_new)
+#define _xdg_mime_parent_list_free             XDG_ENTRY(parent_list_free)
+#define _xdg_mime_parent_list_lookup           XDG_ENTRY(parent_list_lookup)
+#endif
+
+void          _xdg_mime_parent_read_from_file (XdgParentList *list,
+					       const char    *file_name);
+XdgParentList *_xdg_mime_parent_list_new       (void);
+void           _xdg_mime_parent_list_free      (XdgParentList *list);
+const char   **_xdg_mime_parent_list_lookup    (XdgParentList *list,
+						const char    *mime);
+void           _xdg_mime_parent_list_dump      (XdgParentList *list);
+
+#endif /* __XDG_MIME_PARENT_H__ */
diff --git a/thunar/Makefile.am b/thunar/Makefile.am
index 455e62d46dd8597abed944e504731a699f0aaeaa..2b12f5f1e8ea7fc254e28352d7fb6d3f9ff08a77 100644
--- a/thunar/Makefile.am
+++ b/thunar/Makefile.am
@@ -49,8 +49,14 @@ Thunar_SOURCES =							\
 	thunar-location-bar.h						\
 	thunar-location-buttons.c					\
 	thunar-location-buttons.h					\
+	thunar-location-dialog.c					\
+	thunar-location-dialog.h					\
+	thunar-location-entry.c						\
+	thunar-location-entry.h						\
 	thunar-navigator.c						\
 	thunar-navigator.h						\
+	thunar-path-entry.c						\
+	thunar-path-entry.h						\
 	thunar-preferences.c						\
 	thunar-preferences.h						\
 	thunar-properties-dialog.c					\
diff --git a/thunar/main.c b/thunar/main.c
index 9b3ee446d471e5d5a14d696002d8a00de906f6c6..c847fe983946ef91a6c273b299e6f9d1d70a652b 100644
--- a/thunar/main.c
+++ b/thunar/main.c
@@ -41,20 +41,23 @@ main (int argc, char **argv)
   GtkWidget    *window;
   GError       *error = NULL;
 
+#ifndef G_DISABLE_CHECKS
   /* Do NOT remove this line for now, If something doesn't work,
    * fix your code instead!
    */
   g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+#endif
 
   /* initialize the GLib thread support */
-#if 0
   if (!g_thread_supported ())
     g_thread_init (NULL);
-#endif
 
   /* initialize Gtk+ */
   gtk_init (&argc, &argv);
 
+  /* initialize the ThunarVFS library */
+  thunar_vfs_init ();
+
   path = (argc > 1) ? argv[1] : xfce_get_homedir ();
 
   uri = thunar_vfs_uri_new (path, &error);
diff --git a/thunar/thunar-computer-folder.c b/thunar/thunar-computer-folder.c
index 195cb1157b6d506dee7e4086f7e9194ac16ce8b6..32940c202747057d5e6fabc66d91f986685b6729 100644
--- a/thunar/thunar-computer-folder.c
+++ b/thunar/thunar-computer-folder.c
@@ -30,10 +30,22 @@
 
 
 
+enum
+{
+  PROP_0,
+  PROP_LOADING,
+};
+
+
+
 static void               thunar_computer_folder_class_init             (ThunarComputerFolderClass *klass);
 static void               thunar_computer_folder_folder_init            (ThunarFolderIface         *iface);
 static void               thunar_computer_folder_init                   (ThunarComputerFolder      *computer_folder);
 static void               thunar_computer_folder_finalize               (GObject                   *object);
+static void               thunar_computer_folder_get_property           (GObject                   *object,
+                                                                         guint                      prop_id,
+                                                                         GValue                    *value,
+                                                                         GParamSpec                *pspec);
 static gboolean           thunar_computer_folder_has_parent             (ThunarFile                *file);
 static ThunarFolder      *thunar_computer_folder_open_as_folder         (ThunarFile                *file,
                                                                          GError                   **error);
@@ -45,6 +57,7 @@ static const gchar       *thunar_computer_folder_get_icon_name          (ThunarF
                                                                          GtkIconTheme              *icon_theme);
 static ThunarFile        *thunar_computer_folder_get_corresponding_file (ThunarFolder              *folder);
 static GSList            *thunar_computer_folder_get_files              (ThunarFolder              *folder);
+static gboolean           thunar_computer_folder_get_loading            (ThunarFolder              *folder);
 
 
 
@@ -79,6 +92,7 @@ thunar_computer_folder_class_init (ThunarComputerFolderClass *klass)
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize = thunar_computer_folder_finalize;
+  gobject_class->get_property = thunar_computer_folder_get_property;
 
   thunarfile_class = THUNAR_FILE_CLASS (klass);
   thunarfile_class->has_parent = thunar_computer_folder_has_parent;
@@ -88,6 +102,10 @@ thunar_computer_folder_class_init (ThunarComputerFolderClass *klass)
   thunarfile_class->get_kind = thunar_computer_folder_get_kind;
   thunarfile_class->get_mode = thunar_computer_folder_get_mode;
   thunarfile_class->get_icon_name = thunar_computer_folder_get_icon_name;
+
+  g_object_class_override_property (gobject_class,
+                                    PROP_LOADING,
+                                    "loading");
 }
 
 
@@ -97,6 +115,7 @@ thunar_computer_folder_folder_init (ThunarFolderIface *iface)
 {
   iface->get_corresponding_file = thunar_computer_folder_get_corresponding_file;
   iface->get_files = thunar_computer_folder_get_files;
+  iface->get_loading = thunar_computer_folder_get_loading;
 }
 
 
@@ -127,6 +146,28 @@ thunar_computer_folder_finalize (GObject *object)
 
 
 
+static void
+thunar_computer_folder_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  ThunarFolder *folder = THUNAR_FOLDER (object);
+
+  switch (prop_id)
+    {
+    case PROP_LOADING:
+      g_value_set_boolean (value, thunar_folder_get_loading (folder));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
 static gboolean
 thunar_computer_folder_has_parent (ThunarFile *file)
 {
@@ -227,6 +268,14 @@ thunar_computer_folder_get_files (ThunarFolder *folder)
 
 
 
+static gboolean
+thunar_computer_folder_get_loading (ThunarFolder *folder)
+{
+  return FALSE;
+}
+
+
+
 /**
  * thunar_computer_folder_new:
  * @uri   : the #ThunarVfsURI referring to the computer vfolder.
diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c
index e133472981f92291f08ccc969eecc7650ad039ec..d8a9143e48a4f136472ffe70746d1c000db2b307 100644
--- a/thunar/thunar-file.c
+++ b/thunar/thunar-file.c
@@ -43,37 +43,37 @@ enum
 
 
 
-static void             thunar_file_class_init               (ThunarFileClass        *klass);
-static void             thunar_file_finalize                 (GObject                *object);
-static gboolean         thunar_file_real_has_parent          (ThunarFile             *file);
-static ThunarFile      *thunar_file_real_get_parent          (ThunarFile             *file,
-                                                              GError                **error);
-static ThunarFolder    *thunar_file_real_open_as_folder      (ThunarFile             *file,
-                                                              GError                **error);
-static ExoMimeInfo     *thunar_file_real_get_mime_info       (ThunarFile             *file);
-static const gchar     *thunar_file_real_get_special_name    (ThunarFile             *file);
-static gboolean         thunar_file_real_get_date            (ThunarFile             *file,
-                                                              ThunarFileDateType      date_type,
-                                                              ThunarVfsFileTime      *date_return);
-static gboolean         thunar_file_real_get_size            (ThunarFile             *file,
-                                                              ThunarVfsFileSize      *size_return);
-static ThunarVfsVolume *thunar_file_real_get_volume          (ThunarFile             *file,
-                                                              ThunarVfsVolumeManager *volume_manager);
-static ThunarVfsGroup  *thunar_file_real_get_group           (ThunarFile             *file);
-static ThunarVfsUser   *thunar_file_real_get_user            (ThunarFile             *file);
-static gboolean         thunar_file_real_can_execute         (ThunarFile             *file);
-static gboolean         thunar_file_real_can_read            (ThunarFile             *file);
-static gboolean         thunar_file_real_can_write           (ThunarFile             *file);
-static GList           *thunar_file_real_get_emblem_names    (ThunarFile             *file);
-static void             thunar_file_real_changed             (ThunarFile             *file);
-static ThunarFile      *thunar_file_new_internal             (ThunarVfsURI           *uri,
-                                                              GError                **error);
-static gboolean         thunar_file_denies_access_permission (ThunarFile             *file,
-                                                              ThunarVfsFileMode       usr_permissions,
-                                                              ThunarVfsFileMode       grp_permissions,
-                                                              ThunarVfsFileMode       oth_permissions);
-static void             thunar_file_destroyed                (gpointer                data,
-                                                              GObject                *object);
+static void               thunar_file_class_init               (ThunarFileClass        *klass);
+static void               thunar_file_finalize                 (GObject                *object);
+static gboolean           thunar_file_real_has_parent          (ThunarFile             *file);
+static ThunarFile        *thunar_file_real_get_parent          (ThunarFile             *file,
+                                                                GError                **error);
+static ThunarFolder      *thunar_file_real_open_as_folder      (ThunarFile             *file,
+                                                                GError                **error);
+static ThunarVfsMimeInfo *thunar_file_real_get_mime_info       (ThunarFile             *file);
+static const gchar       *thunar_file_real_get_special_name    (ThunarFile             *file);
+static gboolean           thunar_file_real_get_date            (ThunarFile             *file,
+                                                                ThunarFileDateType      date_type,
+                                                                ThunarVfsFileTime      *date_return);
+static gboolean           thunar_file_real_get_size            (ThunarFile             *file,
+                                                                ThunarVfsFileSize      *size_return);
+static ThunarVfsVolume   *thunar_file_real_get_volume          (ThunarFile             *file,
+                                                                ThunarVfsVolumeManager *volume_manager);
+static ThunarVfsGroup    *thunar_file_real_get_group           (ThunarFile             *file);
+static ThunarVfsUser     *thunar_file_real_get_user            (ThunarFile             *file);
+static gboolean           thunar_file_real_can_execute         (ThunarFile             *file);
+static gboolean           thunar_file_real_can_read            (ThunarFile             *file);
+static gboolean           thunar_file_real_can_write           (ThunarFile             *file);
+static GList             *thunar_file_real_get_emblem_names    (ThunarFile             *file);
+static void               thunar_file_real_changed             (ThunarFile             *file);
+static ThunarFile        *thunar_file_new_internal             (ThunarVfsURI           *uri,
+                                                                GError                **error);
+static gboolean           thunar_file_denies_access_permission (ThunarFile             *file,
+                                                                ThunarVfsFileMode       usr_permissions,
+                                                                ThunarVfsFileMode       grp_permissions,
+                                                                ThunarVfsFileMode       oth_permissions);
+static void               thunar_file_destroyed                (gpointer                data,
+                                                                GObject                *object);
 
 
 
@@ -268,7 +268,7 @@ thunar_file_real_open_as_folder (ThunarFile *file,
 
 
 
-static ExoMimeInfo*
+static ThunarVfsMimeInfo*
 thunar_file_real_get_mime_info (ThunarFile *file)
 {
   return NULL;
@@ -476,6 +476,69 @@ thunar_file_destroyed (gpointer data,
 
 
 
+/**
+ * _thunar_file_cache_lookup:
+ * @uri : a #ThunarVfsURI.
+ *
+ * Checks if the #ThunarFile which handles @uri is
+ * already present in the #ThunarFile cache. Returns
+ * a reference to the cached #ThunarFile or %NULL
+ * if no matching file is found.
+ *
+ * No reference on the returned object is taken for
+ * the caller.
+ *
+ * This function is solely intended to be used by
+ * implementors of the #ThunarFile class.
+ *
+ * Return value: the #ThunarFile cached for
+ *               @uri or %NULL.
+ **/
+ThunarFile*
+_thunar_file_cache_lookup (ThunarVfsURI *uri)
+{
+  g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
+
+  /* allocate the ThunarFile cache on-demand */
+  if (G_UNLIKELY (file_cache == NULL))
+    {
+      file_cache = g_hash_table_new (thunar_vfs_uri_hash,
+                                     thunar_vfs_uri_equal);
+    }
+
+  return g_hash_table_lookup (file_cache, uri);
+}
+
+
+
+/**
+ * _thunar_file_cache_insert:
+ * @file : a #ThunarFile.
+ *
+ * Inserts the @file into the #ThunarFile cache and
+ * removes the floating reference from the @file.
+ **/
+void
+_thunar_file_cache_insert (ThunarFile *file)
+{
+  ThunarVfsURI *uri;
+
+  g_return_if_fail (THUNAR_IS_FILE (file));
+  g_return_if_fail (file_cache != NULL);
+
+  /* drop the floating reference */
+  g_assert (GTK_OBJECT_FLOATING (file));
+  g_object_ref (G_OBJECT (file));
+  gtk_object_sink (GTK_OBJECT (file));
+
+  /* insert the file into the cache */
+  uri = thunar_file_get_uri (file);
+  g_object_weak_ref (G_OBJECT (file), thunar_file_destroyed, uri);
+  g_hash_table_insert (file_cache, thunar_vfs_uri_ref (uri), file);
+}
+
+
+
 /**
  * thunar_file_get_for_uri:
  * @uri   : an #ThunarVfsURI instance.
@@ -501,30 +564,14 @@ thunar_file_get_for_uri (ThunarVfsURI *uri,
   g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  /* allocate the ThunarFile cache on-demand */
-  if (G_UNLIKELY (file_cache == NULL))
-    {
-      file_cache = g_hash_table_new (thunar_vfs_uri_hash,
-                                     thunar_vfs_uri_equal);
-    }
-
   /* see if we have the corresponding file cached already */
-  file = g_hash_table_lookup (file_cache, uri);
+  file = _thunar_file_cache_lookup (uri);
   if (file == NULL)
     {
       /* allocate the new file object */
       file = thunar_file_new_internal (uri, error);
       if (G_LIKELY (file != NULL))
-        {
-          /* drop the floating reference */
-          g_assert (GTK_OBJECT_FLOATING (file));
-          g_object_ref (G_OBJECT (file));
-          gtk_object_sink (GTK_OBJECT (file));
-
-          /* insert the file into the cache */
-          g_object_weak_ref (G_OBJECT (file), thunar_file_destroyed, uri);
-          g_hash_table_insert (file_cache, thunar_vfs_uri_ref (uri), file);
-        }
+        _thunar_file_cache_insert (file);
     }
   else
     {
@@ -645,12 +692,12 @@ thunar_file_get_uri (ThunarFile *file)
  * Therefore your component must be able to handle this case!
  *
  * This method automatically takes a reference on the returned
- * object for the caller, so you'll need to call #g_object_unref()
+ * object for the caller, so you'll need to call #thunar_vfs_mime_info()
  * when you are done with it.
  *
  * Return value: the MIME type or %NULL.
  **/
-ExoMimeInfo*
+ThunarVfsMimeInfo*
 thunar_file_get_mime_info (ThunarFile *file)
 {
   g_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h
index 093e7be24ca1474c368921bcd350f5a927eeb3bb..d62f74858d3689c5d2357b8631b444cdbf98acb4 100644
--- a/thunar/thunar-file.h
+++ b/thunar/thunar-file.h
@@ -74,7 +74,7 @@ struct _ThunarFileClass
 
   ThunarVfsURI        *(*get_uri)             (ThunarFile             *file);
 
-  ExoMimeInfo         *(*get_mime_info)       (ThunarFile             *file);
+  ThunarVfsMimeInfo   *(*get_mime_info)       (ThunarFile             *file);
 
   const gchar         *(*get_display_name)    (ThunarFile             *file);
   const gchar         *(*get_special_name)    (ThunarFile             *file);
@@ -121,6 +121,9 @@ struct _ThunarFile
 
 GType              thunar_file_get_type         (void) G_GNUC_CONST;
 
+ThunarFile        *_thunar_file_cache_lookup    (ThunarVfsURI           *uri);
+void               _thunar_file_cache_insert    (ThunarFile             *file);
+
 ThunarFile        *thunar_file_get_for_uri      (ThunarVfsURI           *uri,
                                                  GError                **error);
 
@@ -133,7 +136,7 @@ ThunarFolder      *thunar_file_open_as_folder   (ThunarFile             *file,
 
 ThunarVfsURI      *thunar_file_get_uri          (ThunarFile             *file);
 
-ExoMimeInfo       *thunar_file_get_mime_info    (ThunarFile             *file);
+ThunarVfsMimeInfo *thunar_file_get_mime_info    (ThunarFile             *file);
 
 const gchar       *thunar_file_get_display_name (ThunarFile             *file);
 const gchar       *thunar_file_get_special_name (ThunarFile             *file);
diff --git a/thunar/thunar-folder.c b/thunar/thunar-folder.c
index 6be0ec5fed395ad8a1e5409992cdcf2dacbb8b03..13b3be37e3a79b9d9d5406c82a43bd7ef79756c3 100644
--- a/thunar/thunar-folder.c
+++ b/thunar/thunar-folder.c
@@ -35,6 +35,7 @@ enum
 
 
 static void thunar_folder_base_init  (gpointer klass);
+static void thunar_folder_class_init (gpointer klass);
 
 
 
@@ -54,7 +55,7 @@ thunar_folder_get_type (void)
         sizeof (ThunarFolderIface),
         (GBaseInitFunc) thunar_folder_base_init,
         NULL,
-        NULL,
+        (GClassInitFunc) thunar_folder_class_init,
         NULL,
         NULL,
         0,
@@ -119,6 +120,25 @@ thunar_folder_base_init (gpointer klass)
 
 
 
+static void
+thunar_folder_class_init (gpointer klass)
+{
+  /**
+   * ThunarFolder:loading:
+   *
+   * Tells whether the contents of the #ThunarFolder are
+   * currently being loaded.
+   **/
+  g_object_interface_install_property (klass,
+                                       g_param_spec_boolean ("loading",
+                                                             _("Loading"),
+                                                             _("Whether the contents of the folder are currently being loaded"),
+                                                             FALSE,
+                                                             EXO_PARAM_READABLE));
+}
+
+
+
 /**
  * thunar_folder_get_corresponding_file:
  * @folder : a #ThunarFolder instance.
@@ -158,6 +178,24 @@ thunar_folder_get_files (ThunarFolder *folder)
 
 
 
+/**
+ * thunar_folder_get_loading:
+ * @folder : a #ThunarFolder instance.
+ *
+ * Tells whether the contents of the @folder are currently
+ * being loaded.
+ *
+ * Return value: %TRUE if @folder is loading, else %FALSE.
+ **/
+gboolean
+thunar_folder_get_loading (ThunarFolder *folder)
+{
+  g_return_val_if_fail (THUNAR_IS_FOLDER (folder), FALSE);
+  return THUNAR_FOLDER_GET_IFACE (folder)->get_loading (folder);
+}
+
+
+
 /**
  * thunar_folder_files_added:
  * @folder : a #ThunarFolder instance.
diff --git a/thunar/thunar-folder.h b/thunar/thunar-folder.h
index bce7c5470db6be2e9a453cd6a6c7bb37ee5341c7..358d2e1b5c319984951b1b7fc91d9be85814a865 100644
--- a/thunar/thunar-folder.h
+++ b/thunar/thunar-folder.h
@@ -38,6 +38,7 @@ struct _ThunarFolderIface
   /* methods */
   ThunarFile *(*get_corresponding_file) (ThunarFolder *folder);
   GSList     *(*get_files)              (ThunarFolder *folder);
+  gboolean    (*get_loading)            (ThunarFolder *folder);
 
   /* signals */
   void (*files_added)   (ThunarFolder *folder,
@@ -50,6 +51,7 @@ GType       thunar_folder_get_type               (void) G_GNUC_CONST;
 
 ThunarFile *thunar_folder_get_corresponding_file (ThunarFolder *folder);
 GSList     *thunar_folder_get_files              (ThunarFolder *folder);
+gboolean    thunar_folder_get_loading            (ThunarFolder *folder);
 
 void        thunar_folder_files_added            (ThunarFolder *folder,
                                                   GSList       *files);
diff --git a/thunar/thunar-list-model.c b/thunar/thunar-list-model.c
index 0516531c617d0c42d57e0dccbfc3de0fc7db1479..8ca746bccf07e16ed113074b9b184913efc48135 100644
--- a/thunar/thunar-list-model.c
+++ b/thunar/thunar-list-model.c
@@ -595,10 +595,10 @@ thunar_list_model_get_value (GtkTreeModel *model,
                              gint          column,
                              GValue       *value)
 {
-  ExoMimeInfo *mime_info;
-  GdkPixbuf   *icon;
-  gchar       *str;
-  Row         *row;
+  ThunarVfsMimeInfo *mime_info;
+  GdkPixbuf         *icon;
+  gchar             *str;
+  Row               *row;
 
   g_return_if_fail (THUNAR_IS_LIST_MODEL (model));
   g_return_if_fail (iter->stamp == (THUNAR_LIST_MODEL (model))->stamp);
@@ -657,8 +657,8 @@ thunar_list_model_get_value (GtkTreeModel *model,
       mime_info = thunar_file_get_mime_info (row->file);
       if (G_LIKELY (mime_info != NULL))
         {
-          g_value_set_static_string (value, exo_mime_info_get_name (mime_info));
-          g_object_unref (G_OBJECT (mime_info));
+          g_value_set_string (value, thunar_vfs_mime_info_get_name (mime_info));
+          thunar_vfs_mime_info_unref (mime_info);
         }
       else
         g_value_set_static_string (value, _("unknown"));
@@ -688,8 +688,8 @@ thunar_list_model_get_value (GtkTreeModel *model,
       mime_info = thunar_file_get_mime_info (row->file);
       if (G_LIKELY (mime_info != NULL))
         {
-          g_value_set_static_string (value, exo_mime_info_get_comment (mime_info));
-          g_object_unref (G_OBJECT (mime_info));
+          g_value_set_string (value, thunar_vfs_mime_info_get_comment (mime_info));
+          thunar_vfs_mime_info_unref (mime_info);
         }
       else
         g_value_set_static_string (value, _("unknown"));
@@ -1359,9 +1359,9 @@ static gint
 sort_by_mime_type (ThunarFile *a,
                    ThunarFile *b)
 {
-  ExoMimeInfo *info_a;
-  ExoMimeInfo *info_b;
-  gint         result;
+  ThunarVfsMimeInfo *info_a;
+  ThunarVfsMimeInfo *info_b;
+  gint               result;
 
   info_a = thunar_file_get_mime_info (a);
   info_b = thunar_file_get_mime_info (b);
@@ -1379,11 +1379,11 @@ sort_by_mime_type (ThunarFile *a,
       return 1;
     }
 
-  result = strcasecmp (exo_mime_info_get_name (info_a),
-                       exo_mime_info_get_name (info_b));
+  result = strcasecmp (thunar_vfs_mime_info_get_name (info_a),
+                       thunar_vfs_mime_info_get_name (info_b));
 
-  g_object_unref (G_OBJECT (info_b));
-  g_object_unref (G_OBJECT (info_a));
+  thunar_vfs_mime_info_unref (info_b);
+  thunar_vfs_mime_info_unref (info_a);
 
   return result;
 }
@@ -1451,9 +1451,9 @@ static gint
 sort_by_type (ThunarFile *a,
               ThunarFile *b)
 {
-  ExoMimeInfo *info_a;
-  ExoMimeInfo *info_b;
-  gint         result;
+  ThunarVfsMimeInfo *info_a;
+  ThunarVfsMimeInfo *info_b;
+  gint               result;
 
   info_a = thunar_file_get_mime_info (a);
   info_b = thunar_file_get_mime_info (b);
@@ -1471,11 +1471,11 @@ sort_by_type (ThunarFile *a,
       return 1;
     }
 
-  result = strcasecmp (exo_mime_info_get_comment (info_a),
-                       exo_mime_info_get_comment (info_b));
+  result = strcasecmp (thunar_vfs_mime_info_get_comment (info_a),
+                       thunar_vfs_mime_info_get_comment (info_b));
 
-  g_object_unref (G_OBJECT (info_b));
-  g_object_unref (G_OBJECT (info_a));
+  thunar_vfs_mime_info_unref (info_b);
+  thunar_vfs_mime_info_unref (info_a);
 
   return result;
 }
@@ -1915,16 +1915,16 @@ gchar*
 thunar_list_model_get_statusbar_text (ThunarListModel *store,
                                       GList           *selected_items)
 {
-  ThunarVfsFileSize size_summary;
-  ThunarVfsFileSize size;
-  ThunarVfsVolume  *volume;
-  ExoMimeInfo      *mime_info;
-  GtkTreeIter       iter;
-  ThunarFile       *file;
-  GList            *lp;
-  gchar            *size_string;
-  gchar            *text;
-  gint              n;
+  ThunarVfsMimeInfo *mime_info;
+  ThunarVfsFileSize  size_summary;
+  ThunarVfsFileSize  size;
+  ThunarVfsVolume   *volume;
+  GtkTreeIter        iter;
+  ThunarFile        *file;
+  GList             *lp;
+  gchar             *size_string;
+  gchar             *text;
+  gint               n;
 
   g_return_val_if_fail (THUNAR_IS_LIST_MODEL (store), NULL);
 
@@ -1961,14 +1961,14 @@ thunar_list_model_get_statusbar_text (ThunarListModel *store,
           if (G_LIKELY (size_string != NULL))
             {
               text = g_strdup_printf (_("\"%s\" (%s) %s"), thunar_file_get_display_name (file), size_string,
-                                      exo_mime_info_get_comment (mime_info));
+                                      thunar_vfs_mime_info_get_comment (mime_info));
             }
           else
             {
               text = g_strdup_printf (_("\"%s\" %s"), thunar_file_get_display_name (file),
-                                      exo_mime_info_get_comment (mime_info));
+                                      thunar_vfs_mime_info_get_comment (mime_info));
             }
-          g_object_unref (G_OBJECT (mime_info));
+          thunar_vfs_mime_info_unref (mime_info);
         }
       else
         {
diff --git a/thunar/thunar-local-file.c b/thunar/thunar-local-file.c
index 8a7b99b6a8a23bf21e4d281cfde1107c93196032..6ff2bcac1ddd5dbe0c6e755e77593e903914d010 100644
--- a/thunar/thunar-local-file.c
+++ b/thunar/thunar-local-file.c
@@ -34,7 +34,7 @@ static ThunarFile        *thunar_local_file_get_parent        (ThunarFile
 static ThunarFolder      *thunar_local_file_open_as_folder    (ThunarFile             *file,
                                                                GError                **error);
 static ThunarVfsURI      *thunar_local_file_get_uri           (ThunarFile             *file);
-static ExoMimeInfo       *thunar_local_file_get_mime_info     (ThunarFile             *file);
+static ThunarVfsMimeInfo *thunar_local_file_get_mime_info     (ThunarFile             *file);
 static const gchar       *thunar_local_file_get_display_name  (ThunarFile             *file);
 static const gchar       *thunar_local_file_get_special_name  (ThunarFile             *file);
 static gboolean           thunar_local_file_get_date          (ThunarFile             *file,
@@ -53,7 +53,6 @@ static const gchar       *thunar_local_file_get_icon_name     (ThunarFile
                                                                GtkIconTheme           *icon_theme);
 static void               thunar_local_file_watch             (ThunarFile             *file);
 static void               thunar_local_file_unwatch           (ThunarFile             *file);
-static void               thunar_local_file_changed           (ThunarFile             *file);
 static void               thunar_local_file_monitor           (ThunarVfsMonitor       *monitor,
                                                                ThunarVfsMonitorEvent   event,
                                                                ThunarVfsInfo          *info,
@@ -72,11 +71,10 @@ struct _ThunarLocalFile
 {
   ThunarFile __parent__;
 
-  gchar        *display_name;
-  ExoMimeInfo  *mime_info;
-  ThunarVfsInfo info;
+  gchar         *display_name;
+  ThunarVfsInfo *info;
 
-  gint          watch_id;
+  gint           watch_id;
 };
 
 
@@ -117,7 +115,6 @@ thunar_local_file_class_init (ThunarLocalFileClass *klass)
   thunarfile_class->get_icon_name = thunar_local_file_get_icon_name;
   thunarfile_class->watch = thunar_local_file_watch;
   thunarfile_class->unwatch = thunar_local_file_unwatch;
-  thunarfile_class->changed = thunar_local_file_changed;
 }
 
 
@@ -125,7 +122,7 @@ thunar_local_file_class_init (ThunarLocalFileClass *klass)
 static void
 thunar_local_file_init (ThunarLocalFile *local_file)
 {
-  thunar_vfs_info_init (&local_file->info);
+  local_file->info = NULL;
 }
 
 
@@ -143,12 +140,9 @@ thunar_local_file_finalize (GObject *object)
     }
 #endif
 
-  /* free the mime info */
-  if (G_LIKELY (local_file->mime_info != NULL))
-    g_object_unref (G_OBJECT (local_file->mime_info));
-
   /* reset the vfs info (freeing the specific data) */
-  thunar_vfs_info_reset (&local_file->info);
+  if (G_LIKELY (local_file->info != NULL))
+    thunar_vfs_info_unref (local_file->info);
 
   /* free the display name */
   g_free (local_file->display_name);
@@ -167,7 +161,7 @@ thunar_local_file_get_parent (ThunarFile *file,
   ThunarFile      *computer_file;
 
   /* the root file system node has 'computer:' as its parent */
-  if (G_UNLIKELY (thunar_vfs_uri_is_root (local_file->info.uri)))
+  if (G_UNLIKELY (thunar_vfs_uri_is_root (local_file->info->uri)))
     {
       computer_uri = thunar_vfs_uri_new ("computer:", error);
       if (G_LIKELY (computer_uri != NULL))
@@ -200,24 +194,15 @@ thunar_local_file_open_as_folder (ThunarFile *file,
 static ThunarVfsURI*
 thunar_local_file_get_uri (ThunarFile *file)
 {
-  return THUNAR_LOCAL_FILE (file)->info.uri;
+  return THUNAR_LOCAL_FILE (file)->info->uri;
 }
 
 
 
-static ExoMimeInfo*
+static ThunarVfsMimeInfo*
 thunar_local_file_get_mime_info (ThunarFile *file)
 {
-  ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
-
-  /* determine the mime type on-demand */
-  if (G_UNLIKELY (local_file->mime_info == NULL))
-    local_file->mime_info = thunar_vfs_info_get_mime_info (&local_file->info);
-
-  /* take a reference for the caller */
-  g_object_ref (G_OBJECT (local_file->mime_info));
-
-  return local_file->mime_info;
+  return thunar_vfs_mime_info_ref (THUNAR_LOCAL_FILE (file)->info->mime_info);
 }
 
 
@@ -228,12 +213,12 @@ thunar_local_file_get_display_name (ThunarFile *file)
   ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
 
   /* root directory is always displayed as 'Filesystem' */
-  if (thunar_vfs_uri_is_root (local_file->info.uri))
+  if (thunar_vfs_uri_is_root (local_file->info->uri))
     return _("Filesystem");
 
   /* determine the display name on-demand */
   if (G_UNLIKELY (local_file->display_name == NULL))
-    local_file->display_name = thunar_vfs_uri_get_display_name (local_file->info.uri);
+    local_file->display_name = thunar_vfs_uri_get_display_name (local_file->info->uri);
 
   return local_file->display_name;
 }
@@ -246,9 +231,9 @@ thunar_local_file_get_special_name (ThunarFile *file)
   ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
 
   /* root and home dir have special names */
-  if (thunar_vfs_uri_is_root (local_file->info.uri))
+  if (thunar_vfs_uri_is_root (local_file->info->uri))
     return _("Filesystem");
-  else if (thunar_vfs_uri_is_home (local_file->info.uri))
+  else if (thunar_vfs_uri_is_home (local_file->info->uri))
     return _("Home");
 
   return thunar_file_get_display_name (file);
@@ -266,15 +251,15 @@ thunar_local_file_get_date (ThunarFile        *file,
   switch (date_type)
     {
     case THUNAR_FILE_DATE_ACCESSED:
-      *date_return = local_file->info.atime;
+      *date_return = local_file->info->atime;
       break;
 
     case THUNAR_FILE_DATE_CHANGED:
-      *date_return = local_file->info.ctime;
+      *date_return = local_file->info->ctime;
       break;
 
     case THUNAR_FILE_DATE_MODIFIED:
-      *date_return = local_file->info.mtime;
+      *date_return = local_file->info->mtime;
       break;
 
     default:
@@ -289,7 +274,7 @@ thunar_local_file_get_date (ThunarFile        *file,
 static ThunarVfsFileMode
 thunar_local_file_get_mode (ThunarFile *file)
 {
-  return THUNAR_LOCAL_FILE (file)->info.mode;
+  return THUNAR_LOCAL_FILE (file)->info->mode;
 }
 
 
@@ -299,10 +284,10 @@ thunar_local_file_get_size (ThunarFile        *file,
                             ThunarVfsFileSize *size_return)
 {
   /* we don't return files sizes for directories, as that's confusing */
-  if (THUNAR_LOCAL_FILE (file)->info.type == THUNAR_VFS_FILE_TYPE_DIRECTORY)
+  if (THUNAR_LOCAL_FILE (file)->info->type == THUNAR_VFS_FILE_TYPE_DIRECTORY)
     return FALSE;
 
-  *size_return = THUNAR_LOCAL_FILE (file)->info.size;
+  *size_return = THUNAR_LOCAL_FILE (file)->info->size;
   return TRUE;
 }
 
@@ -311,7 +296,7 @@ thunar_local_file_get_size (ThunarFile        *file,
 static ThunarVfsFileType
 thunar_local_file_get_kind (ThunarFile *file)
 {
-  return THUNAR_LOCAL_FILE (file)->info.type;
+  return THUNAR_LOCAL_FILE (file)->info->type;
 }
 
 
@@ -320,7 +305,7 @@ static ThunarVfsVolume*
 thunar_local_file_get_volume (ThunarFile             *file,
                               ThunarVfsVolumeManager *volume_manager)
 {
-  return thunar_vfs_volume_manager_get_volume_by_info (volume_manager, &THUNAR_LOCAL_FILE (file)->info);
+  return thunar_vfs_volume_manager_get_volume_by_info (volume_manager, THUNAR_LOCAL_FILE (file)->info);
 }
 
 
@@ -329,7 +314,7 @@ static ThunarVfsGroup*
 thunar_local_file_get_group (ThunarFile *file)
 {
   return thunar_vfs_user_manager_get_group_by_id (THUNAR_LOCAL_FILE_GET_CLASS (file)->user_manager,
-                                                  THUNAR_LOCAL_FILE (file)->info.gid);
+                                                  THUNAR_LOCAL_FILE (file)->info->gid);
 }
 
 
@@ -338,7 +323,7 @@ static ThunarVfsUser*
 thunar_local_file_get_user (ThunarFile *file)
 {
   return thunar_vfs_user_manager_get_user_by_id (THUNAR_LOCAL_FILE_GET_CLASS (file)->user_manager,
-                                                 THUNAR_LOCAL_FILE (file)->info.uid);
+                                                 THUNAR_LOCAL_FILE (file)->info->uid);
 }
 
 
@@ -347,7 +332,7 @@ static GList*
 thunar_local_file_get_emblem_names (ThunarFile *file)
 {
   ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
-  ThunarVfsInfo   *info = &local_file->info;
+  ThunarVfsInfo   *info = local_file->info;
   GList           *emblems = NULL;
 
   if ((info->flags & THUNAR_VFS_FILE_FLAGS_SYMLINK) != 0)
@@ -366,27 +351,24 @@ thunar_local_file_get_icon_name (ThunarFile   *file,
                                  GtkIconTheme *icon_theme)
 {
   ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
-  ExoMimeInfo     *mime_info;
   const gchar     *icon_name;
 
   /* special icon for the root node */
-  if (G_UNLIKELY (thunar_vfs_uri_is_root (local_file->info.uri))
+  if (G_UNLIKELY (thunar_vfs_uri_is_root (local_file->info->uri))
       && gtk_icon_theme_has_icon (icon_theme, "gnome-dev-harddisk"))
     {
       return "gnome-dev-harddisk";
     }
 
   /* special icon for the home node */
-  if (G_UNLIKELY (thunar_vfs_uri_is_home (local_file->info.uri))
+  if (G_UNLIKELY (thunar_vfs_uri_is_home (local_file->info->uri))
       && gtk_icon_theme_has_icon (icon_theme, "gnome-fs-home"))
     {
       return "gnome-fs-home";
     }
 
   /* default is the mime type icon */
-  mime_info = thunar_file_get_mime_info (file);
-  icon_name = exo_mime_info_lookup_icon_name (mime_info, icon_theme);
-  g_object_unref (G_OBJECT (mime_info));
+  icon_name = thunar_vfs_mime_info_lookup_icon_name (local_file->info->mime_info, icon_theme);
 
   return icon_name;
 }
@@ -411,10 +393,14 @@ thunar_local_file_watch (ThunarFile *file)
       g_object_ref (G_OBJECT (monitor));
     }
 
+#if 0
   /* add our VFS info to the monitor */
-  local_file->watch_id = thunar_vfs_monitor_add_info (monitor, &local_file->info,
+  local_file->watch_id = thunar_vfs_monitor_add_info (monitor, local_file->info,
                                                       thunar_local_file_monitor,
                                                       local_file);
+#else
+  (void) &thunar_local_file_monitor;
+#endif
 }
 
 
@@ -422,6 +408,7 @@ thunar_local_file_watch (ThunarFile *file)
 static void
 thunar_local_file_unwatch (ThunarFile *file)
 {
+#if 0
   ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
 
   g_return_if_fail (local_file->watch_id != 0);
@@ -429,6 +416,7 @@ thunar_local_file_unwatch (ThunarFile *file)
   /* remove our VFS info from the monitor */
   thunar_vfs_monitor_remove (monitor, local_file->watch_id);
   local_file->watch_id = 0;
+#endif
 
   /* release our reference on the VFS monitor */
   g_object_unref (G_OBJECT (monitor));
@@ -436,23 +424,6 @@ thunar_local_file_unwatch (ThunarFile *file)
 
 
 
-static void
-thunar_local_file_changed (ThunarFile *file)
-{
-  ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
-
-  /* drop the cached mime type, will be recalculated on-demand */
-  if (G_LIKELY (local_file->mime_info != NULL))
-    {
-      g_object_unref (G_OBJECT (local_file->mime_info));
-      local_file->mime_info = NULL;
-    }
-
-  THUNAR_FILE_CLASS (thunar_local_file_parent_class)->changed (file);
-}
-
-
-
 static void
 thunar_local_file_monitor (ThunarVfsMonitor     *monitor,
                            ThunarVfsMonitorEvent event,
@@ -497,24 +468,76 @@ thunar_local_file_new (ThunarVfsURI *uri,
                        GError      **error)
 {
   ThunarLocalFile *local_file;
+  ThunarVfsInfo   *info;
 
   g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
   g_return_val_if_fail (thunar_vfs_uri_get_scheme (uri) == THUNAR_VFS_URI_SCHEME_FILE, NULL);
 
+  /* query the file info */
+  info = thunar_vfs_info_new_for_uri (uri, error);
+  if (G_UNLIKELY (info == NULL))
+    return NULL;
+
   /* allocate the new object */
   local_file = g_object_new (THUNAR_TYPE_LOCAL_FILE, NULL);
-
-  /* query the file info */
-  if (!thunar_vfs_info_query (&local_file->info, uri, error))
-    {
-      gtk_object_sink (GTK_OBJECT (local_file));
-      return NULL;
-    }
+  local_file->info = info;
 
   return THUNAR_FILE (local_file);
 }
 
 
 
+/**
+ * thunar_local_file_get_for_info:
+ * @info : a #ThunarVfsInfo.
+ *
+ * Looks up the #ThunarLocalFile corresponding to
+ * @info. Use this function if want to specify the
+ * #ThunarVfsInfo to use for a #ThunarLocalFile instead
+ * of #thunar_file_get_for_uri(), which would determine
+ * the #ThunarVfsInfo once again.
+ *
+ * The caller is responsible to call #g_object_unref()
+ * when done with the returned object.
+ *
+ * Return value: the #ThunarLocalFile for @info.
+ **/
+ThunarFile*
+thunar_local_file_get_for_info (ThunarVfsInfo *info)
+{
+  ThunarLocalFile *local_file;
+
+  g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (THUNAR_VFS_IS_URI (info->uri), NULL);
+
+  /* check if we already have a cached version of that file */
+  local_file = THUNAR_LOCAL_FILE (_thunar_file_cache_lookup (info->uri));
+  if (G_UNLIKELY (local_file != NULL))
+    {
+      /* take a reference for the caller */
+      g_object_ref (G_OBJECT (local_file));
+
+      /* apply the new info */
+      if (!thunar_vfs_info_matches (local_file->info, info))
+        {
+          thunar_vfs_info_unref (local_file->info);
+          local_file->info = thunar_vfs_info_ref (info);
+          thunar_file_changed (THUNAR_FILE (local_file));
+        }
+    }
+  else
+    {
+      /* allocate a new object */
+      local_file = g_object_new (THUNAR_TYPE_LOCAL_FILE, NULL);
+      local_file->info = thunar_vfs_info_ref (info);
+
+      /* insert the file into the cache (also strips the
+       * floating reference from the object).
+       */
+      _thunar_file_cache_insert (THUNAR_FILE (local_file));
+    }
+
+  return THUNAR_FILE (local_file);
+}
 
diff --git a/thunar/thunar-local-file.h b/thunar/thunar-local-file.h
index 41aee83403a04f55c49a25e934bf4242bcd77c29..2423786e5fc6a7e5b9559663b5c4c73d4549ea73 100644
--- a/thunar/thunar-local-file.h
+++ b/thunar/thunar-local-file.h
@@ -34,10 +34,12 @@ typedef struct _ThunarLocalFile      ThunarLocalFile;
 #define THUNAR_IS_LOCAL_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), THUNAR_TYPE_LOCAL_FILE))
 #define THUNAR_LOCAL_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_LOCAL_FILE, ThunarLocalFileClass))
 
-GType       thunar_local_file_get_type  (void) G_GNUC_CONST;
+GType       thunar_local_file_get_type     (void) G_GNUC_CONST;
 
-ThunarFile *thunar_local_file_new       (ThunarVfsURI *uri,
-                                         GError      **error);
+ThunarFile *thunar_local_file_new          (ThunarVfsURI  *uri,
+                                            GError       **error);
+
+ThunarFile *thunar_local_file_get_for_info (ThunarVfsInfo *info);
 
 G_END_DECLS;
 
diff --git a/thunar/thunar-local-folder.c b/thunar/thunar-local-folder.c
index fa5f146a6eb0e9face4643556874a32ed1120c7c..e118a3b3c7fd3d35682ce01ff97d31d9a4f312cc 100644
--- a/thunar/thunar-local-folder.c
+++ b/thunar/thunar-local-folder.c
@@ -33,14 +33,28 @@
 
 
 
+enum
+{
+  PROP_0,
+  PROP_LOADING,
+};
+
+
+
 static void        thunar_local_folder_class_init                 (ThunarLocalFolderClass *klass);
 static void        thunar_local_folder_folder_init                (ThunarFolderIface      *iface);
 static void        thunar_local_folder_init                       (ThunarLocalFolder      *local_folder);
+static void        thunar_local_folder_get_property               (GObject                *object,
+                                                                   guint                   prop_id,
+                                                                   GValue                 *value,
+                                                                   GParamSpec             *pspec);
 static void        thunar_local_folder_finalize                   (GObject                *object);
 static ThunarFile *thunar_local_folder_get_corresponding_file     (ThunarFolder           *folder);
 static GSList     *thunar_local_folder_get_files                  (ThunarFolder           *folder);
-static gboolean    thunar_local_folder_rescan                     (ThunarLocalFolder      *local_folder,
-                                                                   GError                **error);
+static gboolean    thunar_local_folder_get_loading                (ThunarFolder           *folder);
+static void        thunar_local_folder_callback                   (ThunarVfsJob           *job,
+                                                                   GSList                 *infos,
+                                                                   gpointer                user_data);
 static void        thunar_local_folder_corresponding_file_changed (ThunarFile             *file,
                                                                    ThunarLocalFolder      *local_folder);
 static void        thunar_local_folder_corresponding_file_destroy (ThunarFile             *file,
@@ -59,6 +73,8 @@ struct _ThunarLocalFolder
 {
   GtkObject __parent__;
 
+  ThunarVfsJob *job;
+
   ThunarFile *corresponding_file;
   GSList     *files;
 
@@ -83,6 +99,11 @@ thunar_local_folder_class_init (ThunarLocalFolderClass *klass)
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize = thunar_local_folder_finalize;
+  gobject_class->get_property = thunar_local_folder_get_property;
+
+  g_object_class_override_property (gobject_class,
+                                    PROP_LOADING,
+                                    "loading");
 }
 
 
@@ -92,6 +113,7 @@ thunar_local_folder_folder_init (ThunarFolderIface *iface)
 {
   iface->get_corresponding_file = thunar_local_folder_get_corresponding_file;
   iface->get_files = thunar_local_folder_get_files;
+  iface->get_loading = thunar_local_folder_get_loading;
 }
 
 
@@ -118,6 +140,13 @@ thunar_local_folder_finalize (GObject *object)
 
   g_return_if_fail (THUNAR_IS_LOCAL_FOLDER (local_folder));
 
+  /* cancel the pending job (if any) */
+  if (G_UNLIKELY (local_folder->job != NULL))
+    {
+      thunar_vfs_job_cancel (local_folder->job);
+      thunar_vfs_job_unref (local_folder->job);
+    }
+
   /* disconnect from the corresponding file */
   if (G_LIKELY (local_folder->corresponding_file != NULL))
     {
@@ -149,96 +178,24 @@ thunar_local_folder_finalize (GObject *object)
 
 
 
-static gboolean
-thunar_local_folder_rescan (ThunarLocalFolder *local_folder,
-                            GError           **error)
+static void
+thunar_local_folder_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
 {
-  ThunarVfsURI  *folder_uri;
-  ThunarVfsURI  *file_uri;
-  const gchar   *name;
-  ThunarFile    *file;
-  GSList        *nfiles = NULL;
-  GSList        *ofiles;
-  GSList        *lp;
-  GDir          *dp;
-
-  /* remember the previously known items */
-  ofiles = local_folder->files;
-  local_folder->files = NULL;
-
-  folder_uri = thunar_file_get_uri (local_folder->corresponding_file);
-  dp = g_dir_open (thunar_vfs_uri_get_path (folder_uri), 0, error);
-  if (G_UNLIKELY (dp == NULL))
-    return FALSE;
-
-  for (;;)
-    {
-      name = g_dir_read_name (dp);
-      if (G_UNLIKELY (name == NULL))
-        break;
-
-      /* ignore ".." and "." entries */
-      if (G_UNLIKELY (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))))
-        continue;
-
-      /* check if the item was already present */
-      for (lp = ofiles; lp != NULL; lp = lp->next)
-        if (exo_str_is_equal (thunar_file_get_name (lp->data), name))
-          break;
-
-      if (lp != NULL)
-        {
-          /* the file was already present, relink to active items list */
-          ofiles = g_slist_remove_link (ofiles, lp);
-          lp->next = local_folder->files;
-          local_folder->files = lp;
-        }
-      else
-        {
-          /* we discovered a new file */
-          file_uri = thunar_vfs_uri_relative (folder_uri, name);
-          file = thunar_file_get_for_uri (file_uri, NULL);
-          thunar_vfs_uri_unref (file_uri);
+  ThunarFolder *folder = THUNAR_FOLDER (object);
 
-          if (G_UNLIKELY (file == NULL))
-            continue;
-
-          nfiles = g_slist_prepend (nfiles, file);
-
-          g_signal_connect_closure_by_id (G_OBJECT (file), local_folder->file_destroy_id,
-                                          0, local_folder->file_destroy_closure, TRUE);
-        }
-    }
-
-  g_dir_close (dp);
-
-  /* handle the left over old-files */
-  if (G_UNLIKELY (ofiles != NULL))
+  switch (prop_id)
     {
-      /* notify listeners about removed files */
-      thunar_folder_files_removed (THUNAR_FOLDER (local_folder), ofiles);
+    case PROP_LOADING:
+      g_value_set_boolean (value, thunar_folder_get_loading (folder));
+      break;
 
-      /* drop old files */
-      for (lp = ofiles; lp != NULL; lp = lp->next)
-        {
-          g_signal_handlers_disconnect_matched (G_OBJECT (lp->data), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE,
-                                                local_folder->file_destroy_id, 0, local_folder->file_destroy_closure,
-                                                NULL, NULL);
-          g_object_unref (G_OBJECT (lp->data));
-        }
-      g_slist_free (ofiles);
-    }
-
-  /* notify listening parties about added files and append the
-   * new files to our internal file list.
-   */
-  if (G_LIKELY (nfiles != NULL))
-    {
-      local_folder->files = g_slist_concat (local_folder->files, nfiles);
-      thunar_folder_files_added (THUNAR_FOLDER (local_folder), nfiles);
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
     }
-
-  return TRUE;
 }
 
 
@@ -259,6 +216,68 @@ thunar_local_folder_get_files (ThunarFolder *folder)
 
 
 
+static gboolean
+thunar_local_folder_get_loading (ThunarFolder *folder)
+{
+  return (THUNAR_LOCAL_FOLDER (folder)->job != NULL);
+}
+
+
+
+static void
+thunar_local_folder_callback (ThunarVfsJob *job,
+                              GSList       *infos,
+                              gpointer      user_data)
+{
+  ThunarLocalFolder *local_folder = THUNAR_LOCAL_FOLDER (user_data);
+  ThunarFile        *file;
+  GSList            *nfiles = NULL;
+  GSList            *lp;
+
+  g_return_if_fail (THUNAR_IS_LOCAL_FOLDER (local_folder));
+  g_return_if_fail (local_folder->job == job);
+
+  GDK_THREADS_ENTER ();
+
+  if (thunar_vfs_job_failed (job))
+    {
+      g_warning ("ThunarLocalFolder error: %s", thunar_vfs_job_get_error (job)->message);
+    }
+  else
+    {
+      /* add the new files */
+      for (lp = infos; lp != NULL; lp = lp->next)
+        {
+          file = thunar_local_file_get_for_info (lp->data);
+          nfiles = g_slist_prepend (nfiles, file);
+          local_folder->files = g_slist_prepend (local_folder->files, file);
+
+          g_signal_connect_closure_by_id (G_OBJECT (file), local_folder->file_destroy_id,
+                                          0, local_folder->file_destroy_closure, TRUE);
+        }
+
+      /* tell the consumers that we have new files */
+      if (G_LIKELY (nfiles != NULL))
+        {
+          thunar_folder_files_added (THUNAR_FOLDER (local_folder), nfiles);
+          g_slist_free (nfiles);
+        }
+    }
+
+  /* we did it, the folder is loaded */
+  if (thunar_vfs_job_finished (job))
+    {
+      thunar_vfs_job_unref (local_folder->job);
+      local_folder->job = NULL;
+
+      g_object_notify (G_OBJECT (local_folder), "loading");
+    }
+
+  GDK_THREADS_LEAVE ();
+}
+
+
+
 static void
 thunar_local_folder_corresponding_file_changed (ThunarFile        *file,
                                                 ThunarLocalFolder *local_folder)
@@ -267,9 +286,11 @@ thunar_local_folder_corresponding_file_changed (ThunarFile        *file,
   g_return_if_fail (THUNAR_IS_LOCAL_FOLDER (local_folder));
   g_return_if_fail (local_folder->corresponding_file == file);
 
-  /* rescan the directory */
+  /* rescan the directory (FIXME) */
+#if 0
   if (!thunar_local_folder_rescan (local_folder, NULL))
     gtk_object_destroy (GTK_OBJECT (local_folder));
+#endif
 }
 
 
@@ -351,12 +372,9 @@ thunar_local_folder_get_for_file (ThunarLocalFile *local_file,
 
       g_object_set_data (G_OBJECT (local_file), "thunar-local-folder", local_folder);
 
-      /* try to scan the new folder */
-      if (G_UNLIKELY (!thunar_local_folder_rescan (local_folder, error)))
-        {
-          g_object_unref (G_OBJECT (local_folder));
-          return NULL;
-        }
+      /* schedule the loading of the folder */
+      local_folder->job = thunar_vfs_job_listdir (thunar_file_get_uri (THUNAR_FILE (local_file)),
+                                                  thunar_local_folder_callback, local_folder);
     }
 
   return THUNAR_FOLDER (local_folder);
diff --git a/thunar/thunar-location-bar.c b/thunar/thunar-location-bar.c
index 9c38bdc6bd9c662c0691a5ed618dbffd254e1f84..c2b0777a7290a9cc67097d834c6fa005b04aa51e 100644
--- a/thunar/thunar-location-bar.c
+++ b/thunar/thunar-location-bar.c
@@ -57,3 +57,29 @@ thunar_location_bar_get_type (void)
 
 
 
+/**
+ * thunar_location_bar_accept_focus:
+ * @location_bar : a #ThunarLocationBar.
+ *
+ * If the implementation of the #ThunarLocationBar interface
+ * supports entering a location into a text widget, then the
+ * text widget will be focused and the method will return
+ * %TRUE. The #ThunarLocationEntry is an example for such
+ * an implementation.
+ *
+ * Else if the implementation offers no way to enter a new
+ * location as text, it will simply return %FALSE here. The
+ * #ThunarLocationButtons class is an example for such an
+ * implementation.
+ *
+ * Return value: %TRUE if the @location_bar gave focus to
+ *               a text entry widget provided by @location_bar,
+ *               else %FALSE.
+ **/
+gboolean
+thunar_location_bar_accept_focus (ThunarLocationBar *location_bar)
+{
+  g_return_val_if_fail (THUNAR_IS_LOCATION_BAR (location_bar), FALSE);
+  return (*THUNAR_LOCATION_BAR_GET_IFACE (location_bar)->accept_focus) (location_bar);
+}
+
diff --git a/thunar/thunar-location-bar.h b/thunar/thunar-location-bar.h
index c34171ee97ca841d23a20fbaa93fb011f13ec8d4..0737bd46ef848739ec47f3377a6862f2e5645b9b 100644
--- a/thunar/thunar-location-bar.h
+++ b/thunar/thunar-location-bar.h
@@ -35,9 +35,14 @@ typedef struct _ThunarLocationBar      ThunarLocationBar;
 struct _ThunarLocationBarIface
 {
   GTypeInterface __parent__;
+
+  /* virtual methods */
+  gboolean (*accept_focus) (ThunarLocationBar *location_bar);
 };
 
-GType    thunar_location_bar_get_type              (void) G_GNUC_CONST;
+GType    thunar_location_bar_get_type     (void) G_GNUC_CONST;
+
+gboolean thunar_location_bar_accept_focus (ThunarLocationBar *location_bar);
 
 G_END_DECLS;
 
diff --git a/thunar/thunar-location-buttons.c b/thunar/thunar-location-buttons.c
index 857b67ebd05f1055f6f6ca18029b75786c91c528..4a0de6a3419c0dbc8053c54b2e9f793617b97df4 100644
--- a/thunar/thunar-location-buttons.c
+++ b/thunar/thunar-location-buttons.c
@@ -63,6 +63,7 @@ static void        thunar_location_buttons_set_property           (GObject
 static ThunarFile *thunar_location_buttons_get_current_directory  (ThunarNavigator            *navigator);
 static void        thunar_location_buttons_set_current_directory  (ThunarNavigator            *navigator,
                                                                    ThunarFile                 *current_directory);
+static gboolean    thunar_location_buttons_accept_focus           (ThunarLocationBar          *location_bar);
 static void        thunar_location_buttons_unmap                  (GtkWidget                  *widget);
 static void        thunar_location_buttons_size_request           (GtkWidget                  *widget,
                                                                    GtkRequisition             *requisition);
@@ -211,6 +212,7 @@ thunar_location_buttons_navigator_init (ThunarNavigatorIface *iface)
 static void
 thunar_location_buttons_location_bar_init (ThunarLocationBarIface *iface)
 {
+  iface->accept_focus = thunar_location_buttons_accept_focus;
 }
 
 
@@ -393,6 +395,14 @@ thunar_location_buttons_set_current_directory (ThunarNavigator *navigator,
 
 
 
+static gboolean
+thunar_location_buttons_accept_focus (ThunarLocationBar *location_bar)
+{
+  return FALSE;
+}
+
+
+
 static void
 thunar_location_buttons_unmap (GtkWidget *widget)
 {
diff --git a/thunar/thunar-location-dialog.c b/thunar/thunar-location-dialog.c
new file mode 100644
index 0000000000000000000000000000000000000000..c912adf1026620a903805796c004526932262a54
--- /dev/null
+++ b/thunar/thunar-location-dialog.c
@@ -0,0 +1,173 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * 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 <thunar/thunar-location-dialog.h>
+#include <thunar/thunar-path-entry.h>
+
+
+
+static void thunar_location_dialog_class_init (ThunarLocationDialogClass *klass);
+static void thunar_location_dialog_init       (ThunarLocationDialog      *location_dialog);
+
+
+
+struct _ThunarLocationDialogClass
+{
+  GtkDialogClass __parent__;
+};
+
+struct _ThunarLocationDialog
+{
+  GtkDialog __parent__;
+
+  GtkWidget *entry;
+};
+
+
+
+G_DEFINE_TYPE (ThunarLocationDialog, thunar_location_dialog, GTK_TYPE_DIALOG);
+
+
+
+static void
+thunar_location_dialog_class_init (ThunarLocationDialogClass *klass)
+{
+}
+
+
+
+static gboolean
+transform_object_to_boolean (const GValue *src_value,
+                             GValue       *dst_value,
+                             gpointer      user_data)
+{
+  g_value_set_boolean (dst_value, (g_value_get_object (src_value) != NULL));
+  return TRUE;
+}
+
+
+
+static void
+thunar_location_dialog_init (ThunarLocationDialog *location_dialog)
+{
+  AtkRelationSet *relations;
+  AtkRelation    *relation;
+  AtkObject      *object;
+  GtkWidget      *cancel_button;
+  GtkWidget      *open_button;
+  GtkWidget      *hbox;
+  GtkWidget      *label;
+
+  gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (location_dialog)->vbox), 2);
+  gtk_container_set_border_width (GTK_CONTAINER (location_dialog), 5);
+  gtk_dialog_set_has_separator (GTK_DIALOG (location_dialog), FALSE);
+  gtk_window_set_default_size (GTK_WINDOW (location_dialog), 350, -1);
+  gtk_window_set_title (GTK_WINDOW (location_dialog), _("Open Location"));
+
+  cancel_button = gtk_dialog_add_button (GTK_DIALOG (location_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+  open_button = gtk_dialog_add_button (GTK_DIALOG (location_dialog), GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT);
+  gtk_window_set_default (GTK_WINDOW (location_dialog), open_button);
+
+  hbox = g_object_new (GTK_TYPE_HBOX,
+                       "border-width", 5,
+                       "spacing", 12,
+                       NULL);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (location_dialog)->vbox), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic (_("_Location:"));
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+
+  location_dialog->entry = thunar_path_entry_new ();
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), location_dialog->entry);
+  gtk_entry_set_activates_default (GTK_ENTRY (location_dialog->entry), TRUE);
+  gtk_box_pack_start (GTK_BOX (hbox), location_dialog->entry, TRUE, TRUE, 0);
+  gtk_widget_show (location_dialog->entry);
+
+  /* set Atk label relation for the entry */
+  object = gtk_widget_get_accessible (location_dialog->entry);
+  relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
+  relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
+  atk_relation_set_add (relations, relation);
+  g_object_unref (G_OBJECT (relation));
+
+  /* the "Open" button is only sensitive if a valid file is entered */
+  exo_binding_new_full (G_OBJECT (location_dialog->entry), "current-file",
+                        G_OBJECT (open_button), "sensitive",
+                        transform_object_to_boolean, NULL, NULL);
+}
+
+
+
+/**
+ * thunar_location_dialog_new:
+ * 
+ * Allocates a new #ThunarLocationDialog instance.
+ *
+ * Return value: the newly allocated #ThunarLocationDialog.
+ **/
+GtkWidget*
+thunar_location_dialog_new (void)
+{
+  return g_object_new (THUNAR_TYPE_LOCATION_DIALOG, NULL);
+}
+
+
+
+/**
+ * thunar_location_dialog_get_selected_file:
+ * @location_dialog : a #ThunarLocationDialog.
+ *
+ * Returns the file selected for the @dialog or
+ * %NULL if the file entered is not valid.
+ *
+ * Return value: the selected #ThunarFile or %NULL.
+ **/
+ThunarFile*
+thunar_location_dialog_get_selected_file (ThunarLocationDialog *location_dialog)
+{
+  g_return_val_if_fail (THUNAR_IS_LOCATION_DIALOG (location_dialog), NULL);
+  return thunar_path_entry_get_current_file (THUNAR_PATH_ENTRY (location_dialog->entry));
+}
+
+
+
+/**
+ * thunar_location_dialog_set_selected_file:
+ * @location_dialog : a #ThunarLocationDialog.
+ * @selected_file   : a #ThunarFile or %NULL.
+ *
+ * Sets the file for @location_dialog to @selected_file.
+ **/
+void
+thunar_location_dialog_set_selected_file (ThunarLocationDialog *location_dialog,
+                                          ThunarFile           *selected_file)
+{
+  g_return_if_fail (THUNAR_IS_LOCATION_DIALOG (location_dialog));
+  g_return_if_fail (selected_file == NULL || THUNAR_IS_FILE (selected_file));
+  thunar_path_entry_set_current_file (THUNAR_PATH_ENTRY (location_dialog->entry), selected_file);
+}
+
+
diff --git a/thunar/thunar-location-dialog.h b/thunar/thunar-location-dialog.h
new file mode 100644
index 0000000000000000000000000000000000000000..0d96efddf8fcb86cf49af747dcfe0bcf4353fd31
--- /dev/null
+++ b/thunar/thunar-location-dialog.h
@@ -0,0 +1,47 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * 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 __THUNAR_LOCATION_DIALOG_H__
+#define __THUNAR_LOCATION_DIALOG_H__
+
+#include <thunar/thunar-file.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarLocationDialogClass ThunarLocationDialogClass;
+typedef struct _ThunarLocationDialog      ThunarLocationDialog;
+
+#define THUNAR_TYPE_LOCATION_DIALOG             (thunar_location_dialog_get_type ())
+#define THUNAR_LOCATION_DIALOG(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_LOCATION_DIALOG, ThunarLocationDialog))
+#define THUNAR_LOCATION_DIALOG_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_LOCATION_DIALOG, ThunarLocationDialogClass))
+#define THUNAR_IS_LOCATION_DIALOG(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_LOCATION_DIALOG))
+#define THUNAR_IS_LOCATION_DIALOG_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_LOCATION_DIALOG))
+#define THUNAR_LOCATION_DIALOG_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_LOCATION_DIALOG, ThunarLocationDialogClass))
+
+GType       thunar_location_dialog_get_type          (void) G_GNUC_CONST;
+
+GtkWidget  *thunar_location_dialog_new               (void);
+
+ThunarFile *thunar_location_dialog_get_selected_file (ThunarLocationDialog *location_dialog);
+void        thunar_location_dialog_set_selected_file (ThunarLocationDialog *location_dialog,
+                                                      ThunarFile           *selected_file);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_LOCATION_DIALOG_H__ */
diff --git a/thunar/thunar-location-entry.c b/thunar/thunar-location-entry.c
new file mode 100644
index 0000000000000000000000000000000000000000..9dfd969afe6a3d8c7eafe165c725fb33da493e63
--- /dev/null
+++ b/thunar/thunar-location-entry.c
@@ -0,0 +1,287 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * 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 <thunar/thunar-location-entry.h>
+#include <thunar/thunar-path-entry.h>
+
+
+
+enum
+{
+  PROP_0,
+  PROP_CURRENT_DIRECTORY,
+};
+
+
+
+static void        thunar_location_entry_class_init            (ThunarLocationEntryClass *klass);
+static void        thunar_location_entry_navigator_init        (ThunarNavigatorIface     *iface);
+static void        thunar_location_entry_location_bar_init     (ThunarLocationBarIface   *iface);
+static void        thunar_location_entry_init                  (ThunarLocationEntry      *location_entry);
+static void        thunar_location_entry_finalize              (GObject                  *object);
+static void        thunar_location_entry_get_property          (GObject                  *object,
+                                                                guint                     prop_id,
+                                                                GValue                   *value,
+                                                                GParamSpec               *pspec);
+static void        thunar_location_entry_set_property          (GObject                  *object,
+                                                                guint                     prop_id,
+                                                                const GValue             *value,
+                                                                GParamSpec               *pspec);
+static ThunarFile *thunar_location_entry_get_current_directory (ThunarNavigator          *navigator);
+static void        thunar_location_entry_set_current_directory (ThunarNavigator          *navigator,
+                                                                ThunarFile               *current_directory);
+static gboolean    thunar_location_entry_accept_focus          (ThunarLocationBar        *location_bar);
+static void        thunar_location_entry_activate              (ThunarLocationEntry      *location_entry);
+
+
+
+struct _ThunarLocationEntryClass
+{
+  GtkHBoxClass __parent__;
+};
+
+struct _ThunarLocationEntry
+{
+  GtkHBox __parent__;
+
+  ThunarFile *current_directory;
+  GtkWidget  *path_entry;
+};
+
+
+
+G_DEFINE_TYPE_WITH_CODE (ThunarLocationEntry,
+                         thunar_location_entry,
+                         GTK_TYPE_HBOX,
+                         G_IMPLEMENT_INTERFACE (THUNAR_TYPE_NAVIGATOR,
+                                                thunar_location_entry_navigator_init)
+                         G_IMPLEMENT_INTERFACE (THUNAR_TYPE_LOCATION_BAR,
+                                                thunar_location_entry_location_bar_init));
+
+
+
+static void
+thunar_location_entry_class_init (ThunarLocationEntryClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = thunar_location_entry_finalize;
+  gobject_class->get_property = thunar_location_entry_get_property;
+  gobject_class->set_property = thunar_location_entry_set_property;
+
+  g_object_class_override_property (gobject_class,
+                                    PROP_CURRENT_DIRECTORY,
+                                    "current-directory");
+}
+
+
+
+static void
+thunar_location_entry_navigator_init (ThunarNavigatorIface *iface)
+{
+  iface->get_current_directory = thunar_location_entry_get_current_directory;
+  iface->set_current_directory = thunar_location_entry_set_current_directory;
+}
+
+
+
+static void
+thunar_location_entry_location_bar_init (ThunarLocationBarIface *iface)
+{
+  iface->accept_focus = thunar_location_entry_accept_focus;
+}
+
+
+
+static gboolean
+transform_object_to_boolean (const GValue *src_value,
+                             GValue       *dst_value,
+                             gpointer      user_data)
+{
+  g_value_set_boolean (dst_value, (g_value_get_object (src_value) != NULL));
+  return TRUE;
+}
+
+
+
+static void
+thunar_location_entry_init (ThunarLocationEntry *location_entry)
+{
+  GtkWidget *button;
+  GtkWidget *image;
+
+  gtk_box_set_spacing (GTK_BOX (location_entry), 2);
+
+  location_entry->path_entry = thunar_path_entry_new ();
+  g_signal_connect_swapped (G_OBJECT (location_entry->path_entry), "activate",
+                            G_CALLBACK (thunar_location_entry_activate), location_entry);
+  gtk_box_pack_start (GTK_BOX (location_entry), location_entry->path_entry, TRUE, TRUE, 0);
+  gtk_widget_show (location_entry->path_entry);
+
+  button = g_object_new (GTK_TYPE_BUTTON, "relief", GTK_RELIEF_NONE, NULL);
+  g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (thunar_location_entry_activate), location_entry);
+  gtk_box_pack_start (GTK_BOX (location_entry), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
+  gtk_container_add (GTK_CONTAINER (button), image);
+  gtk_widget_show (image);
+
+  /* the "Go" button is only sensitive if a valid file is entered */
+  exo_binding_new_full (G_OBJECT (location_entry->path_entry), "current-file",
+                        G_OBJECT (button), "sensitive",
+                        transform_object_to_boolean, NULL, NULL);
+}
+
+
+
+static void
+thunar_location_entry_finalize (GObject *object)
+{
+  ThunarLocationEntry *location_entry = THUNAR_LOCATION_ENTRY (object);
+
+  if (G_LIKELY (location_entry->current_directory != NULL))
+    g_object_unref (G_OBJECT (location_entry->current_directory));
+
+  G_OBJECT_CLASS (thunar_location_entry_parent_class)->finalize (object);
+}
+
+
+
+static void
+thunar_location_entry_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  ThunarNavigator *navigator = THUNAR_NAVIGATOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_CURRENT_DIRECTORY:
+      g_value_set_object (value, thunar_navigator_get_current_directory (navigator));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_location_entry_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  ThunarNavigator *navigator = THUNAR_NAVIGATOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_CURRENT_DIRECTORY:
+      thunar_navigator_set_current_directory (navigator, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static ThunarFile*
+thunar_location_entry_get_current_directory (ThunarNavigator *navigator)
+{
+  return THUNAR_LOCATION_ENTRY (navigator)->current_directory;
+}
+
+
+
+static void
+thunar_location_entry_set_current_directory (ThunarNavigator *navigator,
+                                             ThunarFile      *current_directory)
+{
+  ThunarLocationEntry *location_entry = THUNAR_LOCATION_ENTRY (navigator);
+
+  if (G_LIKELY (location_entry->current_directory != NULL))
+    g_object_unref (G_OBJECT (location_entry->current_directory));
+
+  location_entry->current_directory = current_directory;
+
+  if (G_LIKELY (current_directory != NULL))
+    g_object_ref (G_OBJECT (current_directory));
+
+  thunar_path_entry_set_current_file (THUNAR_PATH_ENTRY (location_entry->path_entry), current_directory);
+
+  g_object_notify (G_OBJECT (location_entry), "current-directory");
+}
+
+
+
+static gboolean
+thunar_location_entry_accept_focus (ThunarLocationBar *location_bar)
+{
+  ThunarLocationEntry *location_entry = THUNAR_LOCATION_ENTRY (location_bar);
+
+  /* select the whole path in the path entry */
+  gtk_editable_select_region (GTK_EDITABLE (location_entry->path_entry), 0, -1);
+
+  /* give the keyboard focus to the path entry */
+  gtk_widget_grab_focus (location_entry->path_entry);
+
+  return TRUE;
+}
+
+
+
+static void
+thunar_location_entry_activate (ThunarLocationEntry *location_entry)
+{
+  ThunarFile *file;
+
+  file = thunar_path_entry_get_current_file (THUNAR_PATH_ENTRY (location_entry->path_entry));
+  if (G_LIKELY (file != NULL))
+    thunar_navigator_change_directory (THUNAR_NAVIGATOR (location_entry), file);
+}
+
+
+
+/**
+ * thunar_location_entry_new:
+ *
+ * Allocates a new #ThunarLocationEntry instance.
+ *
+ * Return value: the newly allocated #ThunarLocationEntry.
+ **/
+GtkWidget*
+thunar_location_entry_new (void)
+{
+  return g_object_new (THUNAR_TYPE_LOCATION_ENTRY, NULL);
+}
+
+
+
diff --git a/thunar/thunar-location-entry.h b/thunar/thunar-location-entry.h
new file mode 100644
index 0000000000000000000000000000000000000000..f892f015b5a02a92268c65d584471a7c9cfa5402
--- /dev/null
+++ b/thunar/thunar-location-entry.h
@@ -0,0 +1,43 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * 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 __THUNAR_LOCATION_ENTRY_H__
+#define __THUNAR_LOCATION_ENTRY_H__
+
+#include <thunar/thunar-location-bar.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarLocationEntryClass ThunarLocationEntryClass;
+typedef struct _ThunarLocationEntry      ThunarLocationEntry;
+
+#define THUNAR_TYPE_LOCATION_ENTRY            (thunar_location_entry_get_type ())
+#define THUNAR_LOCATION_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_LOCATION_ENTRY, ThunarLocationEntry))
+#define THUNAR_LOCATION_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_LOCATION_ENTRY, ThunarLocationEntryClass))
+#define THUNAR_IS_LOCATION_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_LOCATION_ENTRY))
+#define THUNAR_IS_LOCATION_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_LOCATION_ENTRY))
+#define THUNAR_LOCATION_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_LOCATION_ENTRY, ThunarLocationEntryClass))
+
+GType      thunar_location_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget *thunar_location_entry_new      (void);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_LOCATION_ENTRY_H__ */
diff --git a/thunar/thunar-path-entry.c b/thunar/thunar-path-entry.c
new file mode 100644
index 0000000000000000000000000000000000000000..3a7f840affda5a4b005a154b006e1d8fd1621541
--- /dev/null
+++ b/thunar/thunar-path-entry.c
@@ -0,0 +1,606 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * 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
+ *
+ * The icon code is based on ideas from SexyIconEntry, which was written by
+ * Christian Hammond <chipx86@chipx86.com>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <thunar/thunar-path-entry.h>
+
+
+
+#define ICON_MARGIN (2)
+
+
+
+enum
+{
+  PROP_0,
+  PROP_CURRENT_FILE,
+};
+
+
+
+static void     thunar_path_entry_class_init          (ThunarPathEntryClass *klass);
+static void     thunar_path_entry_editable_init       (GtkEditableClass     *iface);
+static void     thunar_path_entry_init                (ThunarPathEntry      *path_entry);
+static void     thunar_path_entry_finalize            (GObject              *object);
+static void     thunar_path_entry_get_property        (GObject              *object,  
+                                                       guint                 prop_id,
+                                                       GValue               *value,
+                                                       GParamSpec           *pspec);
+static void     thunar_path_entry_set_property        (GObject              *object,  
+                                                       guint                 prop_id,
+                                                       const GValue         *value,
+                                                       GParamSpec           *pspec);
+static void     thunar_path_entry_size_request        (GtkWidget            *widget,
+                                                       GtkRequisition       *requisition);
+static void     thunar_path_entry_size_allocate       (GtkWidget            *widget,
+                                                       GtkAllocation        *allocation);
+static void     thunar_path_entry_realize             (GtkWidget            *widget);
+static void     thunar_path_entry_unrealize           (GtkWidget            *widget);
+static gboolean thunar_path_entry_focus               (GtkWidget            *widget,
+                                                       GtkDirectionType      direction);
+static gboolean thunar_path_entry_expose_event        (GtkWidget            *widget,
+                                                       GdkEventExpose       *event);
+static void     thunar_path_entry_activate            (GtkEntry             *entry);
+static void     thunar_path_entry_changed             (GtkEditable          *editable);
+static void     thunar_path_entry_get_borders         (ThunarPathEntry      *path_entry,
+                                                       gint                 *xborder,
+                                                       gint                 *yborder);
+static void     thunar_path_entry_get_text_area_size  (ThunarPathEntry      *path_entry,
+                                                       gint                 *x,
+                                                       gint                 *y,
+                                                       gint                 *width,
+                                                       gint                 *height);
+
+
+
+struct _ThunarPathEntryClass
+{
+  GtkEntryClass __parent__;
+};
+
+struct _ThunarPathEntry
+{
+  GtkEntry __parent__;
+
+  ThunarFile *current_file;
+  GdkWindow  *icon_area;
+};
+
+
+
+static GtkEditableClass *thunar_path_entry_editable_parent_iface;
+
+G_DEFINE_TYPE_WITH_CODE (ThunarPathEntry,
+                         thunar_path_entry,
+                         GTK_TYPE_ENTRY,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
+                                                thunar_path_entry_editable_init));
+
+
+
+static void
+thunar_path_entry_class_init (ThunarPathEntryClass *klass)
+{
+  GtkWidgetClass *gtkwidget_class;
+  GtkEntryClass  *gtkentry_class;
+  GObjectClass   *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = thunar_path_entry_finalize;
+  gobject_class->get_property = thunar_path_entry_get_property;
+  gobject_class->set_property = thunar_path_entry_set_property;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->size_request = thunar_path_entry_size_request;
+  gtkwidget_class->size_allocate = thunar_path_entry_size_allocate;
+  gtkwidget_class->realize = thunar_path_entry_realize;
+  gtkwidget_class->unrealize = thunar_path_entry_unrealize;
+  gtkwidget_class->focus = thunar_path_entry_focus;
+  gtkwidget_class->expose_event = thunar_path_entry_expose_event;
+
+  gtkentry_class = GTK_ENTRY_CLASS (klass);
+  gtkentry_class->activate = thunar_path_entry_activate;
+
+  /**
+   * ThunarPathEntry:current-file:
+   *
+   * The #ThunarFile currently displayed by the path entry or %NULL.
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_CURRENT_FILE,
+                                   g_param_spec_object ("current-file",
+                                                        _("Current file"),
+                                                        _("The currently displayed file"),
+                                                        THUNAR_TYPE_FILE,
+                                                        EXO_PARAM_READWRITE));
+
+  /**
+   * ThunarPathEntry:icon-size:
+   *
+   * The preferred size of the icon displayed in the path entry.
+   **/
+  gtk_widget_class_install_style_property (gtkwidget_class,
+                                           g_param_spec_int ("icon-size",
+                                                             _("Icon size"),
+                                                             _("The icon size for the path entry"),
+                                                             1, G_MAXINT, 16, EXO_PARAM_READABLE));
+}
+
+
+
+static void
+thunar_path_entry_editable_init (GtkEditableClass *iface)
+{
+  thunar_path_entry_editable_parent_iface = g_type_interface_peek_parent (iface);
+
+  iface->changed = thunar_path_entry_changed;
+}
+
+
+
+static void
+thunar_path_entry_init (ThunarPathEntry *path_entry)
+{
+}
+
+
+
+static void
+thunar_path_entry_finalize (GObject *object)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (object);
+
+  if (G_LIKELY (path_entry->current_file != NULL))
+    g_object_unref (G_OBJECT (path_entry->current_file));
+
+  G_OBJECT_CLASS (thunar_path_entry_parent_class)->finalize (object);
+}
+
+
+
+static void
+thunar_path_entry_get_property (GObject    *object,  
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (object);
+
+  switch (prop_id)
+    {
+    case PROP_CURRENT_FILE:
+      g_value_set_object (value, thunar_path_entry_get_current_file (path_entry));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_path_entry_set_property (GObject      *object,  
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (object);
+
+  switch (prop_id)
+    {
+    case PROP_CURRENT_FILE:
+      thunar_path_entry_set_current_file (path_entry, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_path_entry_size_request (GtkWidget      *widget,
+                                GtkRequisition *requisition)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (widget);
+  gint             text_height;
+  gint             icon_size;
+  gint             xborder;
+  gint             yborder;
+
+  gtk_widget_style_get (GTK_WIDGET (widget),
+                        "icon-size", &icon_size,
+                        NULL);
+
+  GTK_WIDGET_CLASS (thunar_path_entry_parent_class)->size_request (widget, requisition);
+
+  thunar_path_entry_get_text_area_size (path_entry, &xborder, &yborder, NULL, &text_height);
+
+  requisition->width += icon_size + xborder + 2 * ICON_MARGIN;
+  requisition->height = 2 * yborder + MAX (icon_size + 2 * ICON_MARGIN, text_height);
+}
+
+
+
+static void
+thunar_path_entry_size_allocate (GtkWidget     *widget,
+                                 GtkAllocation *allocation)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (widget);
+  GtkAllocation    icon_allocation;
+  GtkAllocation    text_allocation;
+  gint             icon_size;
+  gint             text_height;
+  gint             xborder;
+  gint             yborder;
+
+  gtk_widget_style_get (GTK_WIDGET (widget),
+                        "icon-size", &icon_size,
+                        NULL);
+
+  widget->allocation = *allocation;
+
+  thunar_path_entry_get_text_area_size (path_entry, &xborder, &yborder, NULL, &text_height);
+
+  text_allocation.y = yborder;
+  text_allocation.width = allocation->width - icon_size - 2 * xborder - 2 * ICON_MARGIN;
+  text_allocation.height = text_height;
+
+  icon_allocation.y = yborder;
+  icon_allocation.width = icon_size + 2 * ICON_MARGIN;
+  icon_allocation.height = MAX (icon_size + 2 * ICON_MARGIN, text_height);
+
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+    {
+      text_allocation.x = xborder;
+      icon_allocation.x = allocation->width - icon_allocation.width - xborder - 2 * ICON_MARGIN;
+    }
+  else
+    {
+      icon_allocation.x = xborder;
+      text_allocation.x = allocation->width - text_allocation.width - xborder;
+    }
+
+  GTK_WIDGET_CLASS (thunar_path_entry_parent_class)->size_allocate (widget, allocation);
+
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      gdk_window_move_resize (GTK_ENTRY (path_entry)->text_area,
+                              text_allocation.x,
+                              text_allocation.y,
+                              text_allocation.width,
+                              text_allocation.height);
+
+      gdk_window_move_resize (path_entry->icon_area,
+                              icon_allocation.x,
+                              icon_allocation.y,
+                              icon_allocation.width,
+                              icon_allocation.height);
+    }
+}
+
+
+
+static void
+thunar_path_entry_realize (GtkWidget *widget)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (widget);
+  GdkWindowAttr    attributes;
+  gint             attributes_mask;
+  gint             text_height;
+  gint             icon_size;
+  gint             spacing;
+
+  gtk_widget_style_get (GTK_WIDGET (widget),
+                        "icon-size", &icon_size,
+                        NULL);
+
+  GTK_WIDGET_CLASS (thunar_path_entry_parent_class)->realize (widget);
+
+  thunar_path_entry_get_text_area_size (path_entry, NULL, NULL, NULL, &text_height);
+  spacing = widget->requisition.height -text_height;
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = gtk_widget_get_events (widget)
+                        | GDK_BUTTON_PRESS_MASK
+                        | GDK_BUTTON_RELEASE_MASK
+                        | GDK_ENTER_NOTIFY_MASK
+                        | GDK_EXPOSURE_MASK
+                        | GDK_LEAVE_NOTIFY_MASK;
+  attributes.x = widget->allocation.x + widget->allocation.width - icon_size - spacing;
+  attributes.y = widget->allocation.y + (widget->allocation.height - widget->requisition.height) / 2;
+  attributes.width = icon_size + spacing;
+  attributes.height = widget->requisition.height;
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+  path_entry->icon_area = gdk_window_new (widget->window, &attributes, attributes_mask);
+  gdk_window_set_user_data (path_entry->icon_area, widget);
+  gdk_window_set_background (path_entry->icon_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+  gdk_window_show (path_entry->icon_area);
+
+  gtk_widget_queue_resize (widget);
+}
+
+
+
+static void
+thunar_path_entry_unrealize (GtkWidget *widget)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (widget);
+
+  /* destroy the icon window */
+  gdk_window_set_user_data (path_entry->icon_area, NULL);
+  gdk_window_destroy (path_entry->icon_area);
+  path_entry->icon_area = NULL;
+
+  /* let the GtkWidget class do the rest */
+  GTK_WIDGET_CLASS (thunar_path_entry_parent_class)->unrealize (widget);
+}
+
+
+
+static gboolean
+thunar_path_entry_focus (GtkWidget       *widget,
+                         GtkDirectionType direction)
+{
+  return GTK_WIDGET_CLASS (thunar_path_entry_parent_class)->focus (widget, direction);
+}
+
+
+
+static gboolean
+thunar_path_entry_expose_event (GtkWidget      *widget,
+                                GdkEventExpose *event)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (widget);
+  GdkPixbuf       *icon;
+  gint             icon_height;
+  gint             icon_width;
+  gint             icon_size;
+  gint             height;
+  gint             width;
+
+  if (event->window == path_entry->icon_area)
+    {
+      gtk_widget_style_get (GTK_WIDGET (widget),
+                            "icon-size", &icon_size,
+                            NULL);
+
+      gdk_drawable_get_size (GDK_DRAWABLE (path_entry->icon_area), &width, &height);
+
+      gtk_paint_flat_box (widget->style, path_entry->icon_area,
+                          GTK_WIDGET_STATE (widget), GTK_SHADOW_NONE,
+                          NULL, widget, "entry_bg",
+                          0, 0, width, height);
+
+      if (path_entry->current_file == NULL)
+        icon = gtk_widget_render_icon (widget, GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_SMALL_TOOLBAR, "path_entry");
+      else
+        icon = thunar_file_load_icon (path_entry->current_file, icon_size);
+
+      if (G_LIKELY (icon != NULL))
+        {
+          icon_width = gdk_pixbuf_get_width (icon);
+          icon_height = gdk_pixbuf_get_height (icon);
+
+          gdk_draw_pixbuf (path_entry->icon_area,
+                           widget->style->black_gc,
+                           icon, 0, 0,
+                           (width - icon_width) / 2,
+                           (height - icon_height) / 2,
+                           icon_width, icon_height,
+                           GDK_RGB_DITHER_NORMAL, 0, 0);
+
+          g_object_unref (G_OBJECT (icon));
+        }
+    }
+  else
+    {
+      return GTK_WIDGET_CLASS (thunar_path_entry_parent_class)->expose_event (widget, event);
+    }
+
+  return TRUE;
+}
+
+
+
+static void
+thunar_path_entry_activate (GtkEntry *entry)
+{
+  GTK_ENTRY_CLASS (thunar_path_entry_parent_class)->activate (entry);
+}
+
+
+
+static void
+thunar_path_entry_changed (GtkEditable *editable)
+{
+  ThunarPathEntry *path_entry = THUNAR_PATH_ENTRY (editable);
+  ThunarVfsURI    *uri;
+  const gchar     *text;
+  ThunarFile      *file;
+
+  text = gtk_entry_get_text (GTK_ENTRY (editable));
+  uri = thunar_vfs_uri_new (text, NULL);
+  file = (uri != NULL) ? thunar_file_get_for_uri (uri, NULL) : NULL;
+
+  if (file != path_entry->current_file)
+    {
+      if (G_UNLIKELY (path_entry->current_file != NULL))
+        g_object_unref (G_OBJECT (path_entry->current_file));
+
+      path_entry->current_file = file;
+
+      if (G_UNLIKELY (file != NULL))
+        g_object_ref (G_OBJECT (file));
+
+      g_object_notify (G_OBJECT (path_entry), "current-file");
+    }
+
+  if (G_UNLIKELY (file != NULL))
+    g_object_unref (G_OBJECT (file));
+  if (G_UNLIKELY (uri != NULL))
+    thunar_vfs_uri_unref (uri);
+}
+
+
+
+static void
+thunar_path_entry_get_borders (ThunarPathEntry *path_entry,
+                               gint            *xborder,
+                               gint            *yborder)
+{
+	gboolean interior_focus;
+	gint     focus_width;
+
+	gtk_widget_style_get (GTK_WIDGET (path_entry),
+                        "focus-line-width", &focus_width,
+                        "interior-focus", &interior_focus,
+                        NULL);
+
+	if (gtk_entry_get_has_frame (GTK_ENTRY (path_entry)))
+    {
+		  *xborder = GTK_WIDGET (path_entry)->style->xthickness;
+  		*yborder = GTK_WIDGET (path_entry)->style->ythickness;
+	  }
+	else
+	  {
+  		*xborder = 0;
+	  	*yborder = 0;
+  	}
+
+	if (!interior_focus)
+	  {
+  		*xborder += focus_width;
+	  	*yborder += focus_width;
+  	}
+}
+
+
+
+static void
+thunar_path_entry_get_text_area_size (ThunarPathEntry *path_entry,
+                                      gint            *x,
+                                      gint            *y,
+                                      gint            *width,
+                                      gint            *height)
+{
+	GtkRequisition requisition;
+	GtkWidget     *widget = GTK_WIDGET (path_entry);
+	gint           xborder;
+  gint           yborder;
+
+	gtk_widget_get_child_requisition (widget, &requisition);
+
+  thunar_path_entry_get_borders (path_entry, &xborder, &yborder);
+
+	if (x != NULL) *x = xborder;
+	if (y != NULL) *y = yborder;
+	if (width  != NULL) *width  = widget->allocation.width - xborder * 2;
+	if (height != NULL) *height = requisition.height - yborder * 2;
+}
+
+
+
+/**
+ * thunar_path_entry_new:
+ * 
+ * Allocates a new #ThunarPathEntry instance.
+ *
+ * Return value: the newly allocated #ThunarPathEntry.
+ **/
+GtkWidget*
+thunar_path_entry_new (void)
+{
+  return g_object_new (THUNAR_TYPE_PATH_ENTRY, NULL);
+}
+
+
+
+/**
+ * thunar_path_entry_get_current_file:
+ * @path_entry : a #ThunarPathEntry.
+ *
+ * Returns the #ThunarFile currently being displayed by
+ * @path_entry or %NULL if @path_entry doesn't contain
+ * a valid #ThunarFile.
+ *
+ * Return value: the #ThunarFile for @path_entry or %NULL.
+ **/
+ThunarFile*
+thunar_path_entry_get_current_file (ThunarPathEntry *path_entry)
+{
+  g_return_val_if_fail (THUNAR_IS_PATH_ENTRY (path_entry), NULL);
+  return path_entry->current_file;
+}
+
+
+
+/**
+ * thunar_path_entry_set_current_file:
+ * @path_entry   : a #ThunarPathEntry.
+ * @current_file : a #ThunarFile or %NULL.
+ *
+ * Sets the #ThunarFile that should be displayed by
+ * @path_entry to @current_file.
+ **/
+void
+thunar_path_entry_set_current_file (ThunarPathEntry *path_entry,
+                                    ThunarFile      *current_file)
+{
+  ThunarVfsURI *uri;
+  gchar        *uri_string;
+
+  g_return_if_fail (THUNAR_IS_PATH_ENTRY (path_entry));
+  g_return_if_fail (current_file == NULL || THUNAR_IS_FILE (current_file));
+
+  uri = (current_file != NULL) ? thunar_file_get_uri (current_file) : NULL;
+  if (G_UNLIKELY (uri == NULL))
+    {
+      gtk_entry_set_text (GTK_ENTRY (path_entry), "");
+    }
+  else if (thunar_vfs_uri_get_scheme (uri) == THUNAR_VFS_URI_SCHEME_FILE)
+    {
+      gtk_entry_set_text (GTK_ENTRY (path_entry), thunar_vfs_uri_get_path (uri));
+    }
+  else
+    {
+      uri_string = thunar_vfs_uri_to_string (uri, THUNAR_VFS_URI_HIDE_HOST);
+      gtk_entry_set_text (GTK_ENTRY (path_entry), uri_string);
+      g_free (uri_string);
+    }
+
+  gtk_editable_set_position (GTK_EDITABLE (path_entry), -1);
+
+  gtk_widget_queue_draw (GTK_WIDGET (path_entry));
+}
+
diff --git a/thunar/thunar-path-entry.h b/thunar/thunar-path-entry.h
new file mode 100644
index 0000000000000000000000000000000000000000..d3ce0812970bdf4827503122b2529c6ca8b81f8b
--- /dev/null
+++ b/thunar/thunar-path-entry.h
@@ -0,0 +1,47 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
+ *
+ * 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 __THUNAR_PATH_ENTRY_H__
+#define __THUNAR_PATH_ENTRY_H__
+
+#include <thunar/thunar-file.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarPathEntryClass ThunarPathEntryClass;
+typedef struct _ThunarPathEntry      ThunarPathEntry;
+
+#define THUNAR_TYPE_PATH_ENTRY            (thunar_path_entry_get_type ())
+#define THUNAR_PATH_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_PATH_ENTRY, ThunarPathEntry))
+#define THUNAR_PATH_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_PATH_ENTRY, ThunarPathEntryClass))
+#define THUNAR_IS_PATH_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_PATH_ENTRY))
+#define THUNAR_IS_PATH_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_PATH_ENTRY))
+#define THUNAR_PATH_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_PATH_ENTRY, ThunarPathEntryClass))
+
+GType       thunar_path_entry_get_type         (void) G_GNUC_CONST;
+
+GtkWidget  *thunar_path_entry_new              (void);
+
+ThunarFile *thunar_path_entry_get_current_file (ThunarPathEntry *path_entry);
+void        thunar_path_entry_set_current_file (ThunarPathEntry *path_entry,
+                                                ThunarFile      *current_file);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_PATH_ENTRY_H__ */
diff --git a/thunar/thunar-properties-dialog.c b/thunar/thunar-properties-dialog.c
index 91535d43cb530f9cdf0d31adbbbcaa5b0bfbe6f3..49f826d9ed39239e2e43df631c1d196107c514f0 100644
--- a/thunar/thunar-properties-dialog.c
+++ b/thunar/thunar-properties-dialog.c
@@ -397,9 +397,9 @@ thunar_properties_dialog_update (ThunarPropertiesDialog *dialog)
 {
   ThunarIconFactory *icon_factory;
   ThunarVfsFileSize  size;
+  ThunarVfsMimeInfo *info;
   ThunarVfsVolume   *volume;
   GtkIconTheme      *icon_theme;
-  ExoMimeInfo       *info;
   const gchar       *icon_name;
   const gchar       *name;
   GdkPixbuf         *icon;
@@ -430,9 +430,9 @@ thunar_properties_dialog_update (ThunarPropertiesDialog *dialog)
   info = thunar_file_get_mime_info (dialog->file);
   if (G_LIKELY (info != NULL))
     {
-      gtk_label_set_text (GTK_LABEL (dialog->kind_label), exo_mime_info_get_comment (info));
+      gtk_label_set_text (GTK_LABEL (dialog->kind_label), thunar_vfs_mime_info_get_comment (info));
       gtk_widget_show (dialog->kind_label);
-      g_object_unref (G_OBJECT (info));
+      thunar_vfs_mime_info_unref (info);
     }
   else
     {
diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c
index be209d8612321ea60d8db98ed114388564942750..96c8b3d2e7cfdfef6c78fff38e2345336679903a 100644
--- a/thunar/thunar-standard-view.c
+++ b/thunar/thunar-standard-view.c
@@ -77,8 +77,7 @@ static void          thunar_standard_view_action_paste              (GtkAction
                                                                      ThunarStandardView       *standard_view);
 static void          thunar_standard_view_action_show_hidden_files  (GtkToggleAction          *toggle_action,
                                                                      ThunarStandardView       *standard_view);
-static gboolean      thunar_standard_view_loading_idle              (gpointer                  user_data);
-static void          thunar_standard_view_loading_idle_destroy      (gpointer                  user_data);
+static void          thunar_standard_view_loading_unbound           (gpointer                  user_data);
 
 
 
@@ -170,9 +169,15 @@ thunar_standard_view_class_init (ThunarStandardViewClass *klass)
                                     PROP_CURRENT_DIRECTORY,
                                     "current-directory");
 
-  g_object_class_override_property (gobject_class,
-                                    PROP_LOADING,
-                                    "loading");
+  g_object_class_install_property (gobject_class,
+                                   PROP_LOADING,
+                                   g_param_spec_override ("loading",
+                                                          g_param_spec_boolean ("loading",
+                                                                                _("Loading"),
+                                                                                _("Whether the view is currently being loaded"),
+                                                                                FALSE,
+                                                                                EXO_PARAM_READWRITE)));
+
   g_object_class_override_property (gobject_class,
                                     PROP_STATUSBAR_TEXT,
                                     "statusbar-text");
@@ -223,7 +228,6 @@ thunar_standard_view_init (ThunarStandardView *standard_view)
                                        GTK_WIDGET (standard_view));
 
   standard_view->model = thunar_list_model_new ();
-  standard_view->loading_idle_id = -1;
 
   /* be sure to update the statusbar text whenever the number of
    * files in our model changes.
@@ -264,9 +268,9 @@ thunar_standard_view_dispose (GObject *object)
 {
   ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (object);
 
-  /* be sure to cancel any loading idle source */
-  if (G_UNLIKELY (standard_view->loading_idle_id >= 0))
-    g_source_remove (standard_view->loading_idle_id);
+  /* unregister the "loading" binding */
+  if (G_UNLIKELY (standard_view->loading_binding != NULL))
+    exo_binding_unbind (standard_view->loading_binding);
 
   /* reset the UI manager property */
   thunar_view_set_ui_manager (THUNAR_VIEW (standard_view), NULL);
@@ -282,7 +286,7 @@ thunar_standard_view_finalize (GObject *object)
   ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (object);
 
   /* some safety checks */
-  g_assert (standard_view->loading_idle_id < 0);
+  g_assert (standard_view->loading_binding == NULL);
   g_assert (standard_view->ui_manager == NULL);
   g_assert (standard_view->clipboard == NULL);
 
@@ -339,12 +343,25 @@ thunar_standard_view_set_property (GObject      *object,
                                    const GValue *value,
                                    GParamSpec   *pspec)
 {
+  ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (object);
+  gboolean            loading;
+
   switch (prop_id)
     {
     case PROP_CURRENT_DIRECTORY:
       thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (object), g_value_get_object (value));
       break;
 
+    case PROP_LOADING:
+      loading = g_value_get_boolean (value);
+      if (G_LIKELY (loading != standard_view->loading))
+        {
+          standard_view->loading = loading;
+          g_object_notify (object, "loading");
+          g_object_notify (object, "statusbar-text");
+        }
+      break;
+
     case PROP_UI_MANAGER:
       thunar_view_set_ui_manager (THUNAR_VIEW (object), g_value_get_object (value));
       break;
@@ -427,6 +444,10 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator,
   g_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view));
   g_return_if_fail (current_directory == NULL || THUNAR_IS_FILE (current_directory));
 
+  /* disconnect any previous "loading" binding */
+  if (G_LIKELY (standard_view->loading_binding != NULL))
+    exo_binding_unbind (standard_view->loading_binding);
+
   /* check if we want to reset the directory */
   if (G_UNLIKELY (current_directory == NULL))
     {
@@ -434,32 +455,10 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator,
       return;
     }
 
-  /* setup the folder for the view, we use a simple but very effective
-   * trick here to speed up the folder change: we completely disconnect
-   * the model from the view, load the folder into the model and afterwards
-   * reconnect the model with the view. This way we avoid having to fire
-   * and process thousands of row_removed() and row_inserted() signals.
-   * Instead the view can process the complete file list in the model
-   * ONCE.
+  /* We drop the model from the view as a simple optimization to speed up
+   * the process of disconnecting the model data from the view.
    */
-  g_object_set (G_OBJECT (GTK_BIN (standard_view)->child),
-                "model", NULL,
-                NULL);
-
-  /* give the real view some time to apply the new (not existing!) model */
-  while (gtk_events_pending ())
-    gtk_main_iteration ();
-
-  /* enter loading state (if not already in loading state) */
-  if (G_LIKELY (standard_view->loading_idle_id < 0))
-    {
-      /* launch the idle function, which will reset the loading state */
-      standard_view->loading_idle_id = g_idle_add_full (G_PRIORITY_LOW + 100, thunar_standard_view_loading_idle,
-                                                        standard_view, thunar_standard_view_loading_idle_destroy);
-
-      /* tell everybody that we are loading */
-      g_object_notify (G_OBJECT (standard_view), "loading");
-    }
+  g_object_set (G_OBJECT (GTK_BIN (standard_view)->child), "model", NULL, NULL);
 
   /* try to open the new directory */
   folder = thunar_file_open_as_folder (current_directory, &error);
@@ -490,15 +489,19 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator,
     }
   else
     {
+      /* connect the "loading" binding */
+      standard_view->loading_binding = exo_binding_new_full (G_OBJECT (folder), "loading",
+                                                             G_OBJECT (standard_view), "loading",
+                                                             NULL, thunar_standard_view_loading_unbound,
+                                                             standard_view);
+
       /* apply the new folder */
       thunar_list_model_set_folder (standard_view->model, folder);
       g_object_unref (G_OBJECT (folder));
     }
 
-  /* reset the model on the real view */
-  g_object_set (G_OBJECT (GTK_BIN (standard_view)->child),
-                "model", standard_view->model,
-                NULL);
+  /* reconnect our model to the view */
+  g_object_set (G_OBJECT (GTK_BIN (standard_view)->child), "model", standard_view->model, NULL);
 
   /* notify all listeners about the new/old current directory */
   g_object_notify (G_OBJECT (standard_view), "current-directory");
@@ -509,7 +512,7 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator,
 static gboolean
 thunar_standard_view_get_loading (ThunarView *view)
 {
-  return (THUNAR_STANDARD_VIEW (view)->loading_idle_id >= 0);
+  return THUNAR_STANDARD_VIEW (view)->loading;
 }
 
 
@@ -527,6 +530,13 @@ thunar_standard_view_get_statusbar_text (ThunarView *view)
     {
       /* query the selected items (actually a list of GtkTreePath's) */
       items = THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->get_selected_items (standard_view);
+
+      /* we display a loading text if no items are
+       * selected and the view is loading
+       */
+      if (items == NULL && standard_view->loading)
+        return _("Loading folder contents...");
+
       standard_view->statusbar_text = thunar_list_model_get_statusbar_text (standard_view->model, items);
       g_list_foreach (items, (GFunc) gtk_tree_path_free, NULL);
       g_list_free (items);
@@ -742,31 +752,21 @@ thunar_standard_view_action_show_hidden_files (GtkToggleAction    *toggle_action
 
 
 
-static gboolean
-thunar_standard_view_loading_idle (gpointer user_data)
-{
-  /* we just return FALSE here, and let the thunar_standard_view_loading_idle_destroy()
-   * method do the real work for us.
-   */
-  return FALSE;
-}
-
-
-
 static void
-thunar_standard_view_loading_idle_destroy (gpointer user_data)
+thunar_standard_view_loading_unbound (gpointer user_data)
 {
   ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (user_data);
 
-  GDK_THREADS_ENTER ();
-
-  /* reset the loading state... */
-  standard_view->loading_idle_id = -1;
+  /* we don't have any binding now */
+  standard_view->loading_binding = NULL;
 
-  /* ...and tell everybody */
-  g_object_notify (G_OBJECT (standard_view), "loading");
-
-  GDK_THREADS_LEAVE ();
+  /* reset the "loading" property */
+  if (G_UNLIKELY (standard_view->loading))
+    {
+      standard_view->loading = FALSE;
+      g_object_notify (G_OBJECT (standard_view), "loading");
+      g_object_notify (G_OBJECT (standard_view), "statusbar-text");
+    }
 }
 
 
diff --git a/thunar/thunar-standard-view.h b/thunar/thunar-standard-view.h
index b15ef471857003b5de785b40eb9ad05c1f8c519c..666a0b689bda7bb4a46f58050cdb104c5ebb43fc 100644
--- a/thunar/thunar-standard-view.h
+++ b/thunar/thunar-standard-view.h
@@ -57,7 +57,8 @@ struct _ThunarStandardView
   GtkUIManager           *ui_manager;
   guint                   ui_merge_id;
 
-  gint                    loading_idle_id;
+  ExoBinding             *loading_binding;
+  gboolean                loading;
 };
 
 GType thunar_standard_view_get_type           (void) G_GNUC_CONST;
diff --git a/thunar/thunar-trash-file.c b/thunar/thunar-trash-file.c
index e6a34fac1248d80759d84d971bda62238505f8a8..4ae872110561fe573a2bb3e3f0cf27ea2de195ae 100644
--- a/thunar/thunar-trash-file.c
+++ b/thunar/thunar-trash-file.c
@@ -32,25 +32,25 @@
 
 
 
-static void              thunar_trash_file_class_init       (ThunarTrashFileClass *klass);
-static void              thunar_trash_file_init             (ThunarTrashFile      *trash_file);
-static void              thunar_trash_file_finalize         (GObject              *object);
-static ThunarFolder     *thunar_trash_file_open_as_folder   (ThunarFile           *file,
-                                                             GError              **error);
-static ThunarVfsURI     *thunar_trash_file_get_uri          (ThunarFile           *file);
-static ExoMimeInfo      *thunar_trash_file_get_mime_info    (ThunarFile           *file);
-static const gchar      *thunar_trash_file_get_display_name (ThunarFile           *file);
-static gboolean          thunar_trash_file_get_date         (ThunarFile           *file,
-                                                             ThunarFileDateType    date_type,
-                                                             ThunarVfsFileTime    *date_return);
-static ThunarVfsFileType thunar_trash_file_get_kind         (ThunarFile           *file);
-static ThunarVfsFileMode thunar_trash_file_get_mode         (ThunarFile           *file);
-static gboolean          thunar_trash_file_get_size         (ThunarFile           *file,
-                                                             ThunarVfsFileSize    *size_return);
-static ThunarVfsGroup   *thunar_trash_file_get_group        (ThunarFile           *file);
-static ThunarVfsUser    *thunar_trash_file_get_user         (ThunarFile           *file);
-static const gchar      *thunar_trash_file_get_icon_name    (ThunarFile           *file,
-                                                             GtkIconTheme         *icon_theme);
+static void               thunar_trash_file_class_init       (ThunarTrashFileClass *klass);
+static void               thunar_trash_file_init             (ThunarTrashFile      *trash_file);
+static void               thunar_trash_file_finalize         (GObject              *object);
+static ThunarFolder      *thunar_trash_file_open_as_folder   (ThunarFile           *file,
+                                                              GError              **error);
+static ThunarVfsURI      *thunar_trash_file_get_uri          (ThunarFile           *file);
+static ThunarVfsMimeInfo *thunar_trash_file_get_mime_info    (ThunarFile           *file);
+static const gchar       *thunar_trash_file_get_display_name (ThunarFile           *file);
+static gboolean           thunar_trash_file_get_date         (ThunarFile           *file,
+                                                              ThunarFileDateType    date_type,
+                                                              ThunarVfsFileTime    *date_return);
+static ThunarVfsFileType  thunar_trash_file_get_kind         (ThunarFile           *file);
+static ThunarVfsFileMode  thunar_trash_file_get_mode         (ThunarFile           *file);
+static gboolean           thunar_trash_file_get_size         (ThunarFile           *file,
+                                                              ThunarVfsFileSize    *size_return);
+static ThunarVfsGroup    *thunar_trash_file_get_group        (ThunarFile           *file);
+static ThunarVfsUser     *thunar_trash_file_get_user         (ThunarFile           *file);
+static const gchar       *thunar_trash_file_get_icon_name    (ThunarFile           *file,
+                                                              GtkIconTheme         *icon_theme);
 
 
 
@@ -152,7 +152,7 @@ thunar_trash_file_get_uri (ThunarFile *file)
 
 
 
-static ExoMimeInfo*
+static ThunarVfsMimeInfo*
 thunar_trash_file_get_mime_info (ThunarFile *file)
 {
   return thunar_file_get_mime_info (THUNAR_TRASH_FILE (file)->real_file);
diff --git a/thunar/thunar-trash-folder.c b/thunar/thunar-trash-folder.c
index b32eae5f22d32f867985306a4e02483ffbdbbe8d..c872eacfae7789e4b3dd37a25c6d44b517b8bce4 100644
--- a/thunar/thunar-trash-folder.c
+++ b/thunar/thunar-trash-folder.c
@@ -27,16 +27,28 @@
 
 
 
+enum
+{
+  PROP_0,
+  PROP_LOADING,
+};
+
+
+
 static void               thunar_trash_folder_class_init              (ThunarTrashFolderClass *klass);
 static void               thunar_trash_folder_folder_init             (ThunarFolderIface      *iface);
 static void               thunar_trash_folder_init                    (ThunarTrashFolder      *trash_folder);
 static void               thunar_trash_folder_finalize                (GObject                *object);
+static void               thunar_trash_folder_get_property            (GObject                *object,
+                                                                       guint                   prop_id,
+                                                                       GValue                 *value,
+                                                                       GParamSpec             *pspec);
 static ThunarFile        *thunar_trash_folder_get_parent              (ThunarFile             *file,
                                                                        GError                **error);
 static ThunarFolder      *thunar_trash_folder_open_as_folder          (ThunarFile             *file,
                                                                        GError                **error);
 static ThunarVfsURI      *thunar_trash_folder_get_uri                 (ThunarFile             *file);
-static ExoMimeInfo       *thunar_trash_folder_get_mime_info           (ThunarFile             *file);
+static ThunarVfsMimeInfo *thunar_trash_folder_get_mime_info           (ThunarFile             *file);
 static const gchar       *thunar_trash_folder_get_display_name        (ThunarFile             *file);
 static ThunarVfsFileType  thunar_trash_folder_get_kind                (ThunarFile             *file);
 static ThunarVfsFileMode  thunar_trash_folder_get_mode                (ThunarFile             *file);
@@ -44,6 +56,7 @@ static const gchar       *thunar_trash_folder_get_icon_name           (ThunarFil
                                                                        GtkIconTheme           *icon_theme);
 static ThunarFile        *thunar_trash_folder_get_corresponding_file  (ThunarFolder           *folder);
 static GSList            *thunar_trash_folder_get_files               (ThunarFolder           *folder);
+static gboolean           thunar_trash_folder_get_loading             (ThunarFolder           *folder);
 
 
 
@@ -79,6 +92,7 @@ thunar_trash_folder_class_init (ThunarTrashFolderClass *klass)
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize = thunar_trash_folder_finalize;
+  gobject_class->get_property = thunar_trash_folder_get_property;
 
   thunarfile_class = THUNAR_FILE_CLASS (klass);
   thunarfile_class->get_parent = thunar_trash_folder_get_parent;
@@ -89,6 +103,10 @@ thunar_trash_folder_class_init (ThunarTrashFolderClass *klass)
   thunarfile_class->get_kind = thunar_trash_folder_get_kind;
   thunarfile_class->get_mode = thunar_trash_folder_get_mode;
   thunarfile_class->get_icon_name = thunar_trash_folder_get_icon_name;
+
+  g_object_class_override_property (gobject_class,
+                                    PROP_LOADING,
+                                    "loading");
 }
 
 
@@ -98,6 +116,7 @@ thunar_trash_folder_folder_init (ThunarFolderIface *iface)
 {
   iface->get_corresponding_file = thunar_trash_folder_get_corresponding_file;
   iface->get_files = thunar_trash_folder_get_files;
+  iface->get_loading = thunar_trash_folder_get_loading;
 }
 
 
@@ -136,6 +155,28 @@ thunar_trash_folder_finalize (GObject *object)
 
 
 
+static void
+thunar_trash_folder_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  ThunarFolder *folder = THUNAR_FOLDER (object);
+
+  switch (prop_id)
+    {
+    case PROP_LOADING:
+      g_value_set_boolean (value, thunar_folder_get_loading (folder));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
 static ThunarFile*
 thunar_trash_folder_get_parent (ThunarFile *file,
                                 GError    **error)
@@ -177,18 +218,10 @@ thunar_trash_folder_get_uri (ThunarFile *file)
 
 
 
-static ExoMimeInfo*
+static ThunarVfsMimeInfo*
 thunar_trash_folder_get_mime_info (ThunarFile *file)
 {
-  ExoMimeDatabase *mime_database;
-  ExoMimeInfo     *mime_info;
-
-  /* the trash vfolder should appear as regular folder */
-  mime_database = exo_mime_database_get_default ();
-  mime_info = exo_mime_database_get_info (mime_database, "inode/directory");
-  g_object_unref (G_OBJECT (mime_database));
-
-  return mime_info;
+  return thunar_vfs_mime_info_get ("inode/directory");
 }
 
 
@@ -282,6 +315,14 @@ thunar_trash_folder_get_files (ThunarFolder *folder)
 
 
 
+static gboolean
+thunar_trash_folder_get_loading (ThunarFolder *folder)
+{
+  return FALSE;
+}
+
+
+
 /**
  * thunar_trash_folder_new:
  * @uri   : the #ThunarVfsURI referrring to the trash file.
diff --git a/thunar/thunar-window-ui.xml b/thunar/thunar-window-ui.xml
index 77c977f37030320f2be0a17d1909b9741adcf523..569ee13cc2c2b77b4cb1993880a7a2260eea13fb 100644
--- a/thunar/thunar-window-ui.xml
+++ b/thunar/thunar-window-ui.xml
@@ -22,11 +22,21 @@
     </menu>
 
     <menu action="view-menu" name="view-menu">
+      <menu action="view-location-bar-menu" name="view-location-bar-menu">
+        <menuitem action="view-location-bar-buttons" name="view-location-bar-buttons" />
+        <menuitem action="view-location-bar-entry" name="view-location-bar-entry" />
+        <menuitem action="view-location-bar-hidden" name="view-location-bar-hidden" />
+      </menu>
+      
+      <separator />
+
       <placeholder name="placeholder-view-display-preferences" />
     </menu>
 
     <menu action="go-menu" name="go-menu">
       <menuitem action="open-parent" name="open-parent" />
+      <separator />
+      <menuitem action="open-location" name="open-location" />
     </menu>
 
     <menu action="help-menu" name="help-menu">
diff --git a/thunar/thunar-window.c b/thunar/thunar-window.c
index e384498057b4703d0400da4c6727bb6f7a071789..1c0c5a8cd7fd931f4d5d7ab0f8e64341ced3f45c 100644
--- a/thunar/thunar-window.c
+++ b/thunar/thunar-window.c
@@ -25,6 +25,8 @@
 #include <thunar/thunar-favourites-pane.h>
 #include <thunar/thunar-icon-view.h>
 #include <thunar/thunar-location-buttons.h>
+#include <thunar/thunar-location-dialog.h>
+#include <thunar/thunar-location-entry.h>
 #include <thunar/thunar-statusbar.h>
 #include <thunar/thunar-window.h>
 #include <thunar/thunar-window-ui.h>
@@ -40,26 +42,31 @@ enum
 
 
 
-static void     thunar_window_class_init          (ThunarWindowClass  *klass);
-static void     thunar_window_init                (ThunarWindow       *window);
-static void     thunar_window_finalize            (GObject            *object);
-static void     thunar_window_get_property        (GObject            *object,
-                                                   guint               prop_id,
-                                                   GValue             *value,
-                                                   GParamSpec         *pspec);
-static void     thunar_window_set_property        (GObject            *object,
-                                                   guint               prop_id,
-                                                   const GValue       *value,
-                                                   GParamSpec         *pspec);
-static void     thunar_window_action_close        (GtkAction          *action,
-                                                   ThunarWindow       *window);
-static void     thunar_window_action_go_up        (GtkAction          *action,
-                                                   ThunarWindow       *window);
-static void     thunar_window_action_about        (GtkAction          *action,
-                                                   ThunarWindow       *window);
-static void     thunar_window_notify_loading      (ThunarView         *view,
-                                                   GParamSpec         *pspec,
-                                                   ThunarWindow       *window);
+static void     thunar_window_class_init                  (ThunarWindowClass  *klass);
+static void     thunar_window_init                        (ThunarWindow       *window);
+static void     thunar_window_finalize                    (GObject            *object);
+static void     thunar_window_get_property                (GObject            *object,
+                                                           guint               prop_id,
+                                                           GValue             *value,
+                                                           GParamSpec         *pspec);
+static void     thunar_window_set_property                (GObject            *object,
+                                                           guint               prop_id,
+                                                           const GValue       *value,
+                                                           GParamSpec         *pspec);
+static void     thunar_window_action_close                (GtkAction          *action,
+                                                           ThunarWindow       *window);
+static void     thunar_window_action_location_bar_changed (GtkRadioAction     *action,
+                                                           GtkRadioAction     *current,
+                                                           ThunarWindow       *window);
+static void     thunar_window_action_go_up                (GtkAction          *action,
+                                                           ThunarWindow       *window);
+static void     thunar_window_action_location             (GtkAction          *action,
+                                                           ThunarWindow       *window);
+static void     thunar_window_action_about                (GtkAction          *action,
+                                                           ThunarWindow       *window);
+static void     thunar_window_notify_loading              (ThunarView         *view,
+                                                           GParamSpec         *pspec,
+                                                           ThunarWindow       *window);
 
 
 
@@ -91,8 +98,10 @@ static const GtkActionEntry const action_entries[] =
   { "close", GTK_STOCK_CLOSE, N_ ("_Close"), "<control>W", N_ ("Close this window"), G_CALLBACK (thunar_window_action_close), },
   { "edit-menu", NULL, N_ ("_Edit"), NULL, },
   { "view-menu", NULL, N_ ("_View"), NULL, },
+  { "view-location-bar-menu", NULL, N_ ("_Location Bar"), NULL, },
   { "go-menu", NULL, N_ ("_Go"), NULL, },
   { "open-parent", GTK_STOCK_GO_UP, N_ ("Open _Parent"), "<alt>Up", N_ ("Open the parent folder"), G_CALLBACK (thunar_window_action_go_up), },
+  { "open-location", NULL, N_ ("Open _Location..."), "<control>L", N_ ("Specify a location to open"), G_CALLBACK (thunar_window_action_location), },
   { "help-menu", NULL, N_ ("_Help"), NULL, },
 #if GTK_CHECK_VERSION(2,6,0)
   { "about", GTK_STOCK_ABOUT, N_ ("_About"), NULL, N_ ("Display information about Thunar"), G_CALLBACK (thunar_window_action_about), },
@@ -152,17 +161,41 @@ thunar_window_class_init (ThunarWindowClass *klass)
 static void
 thunar_window_init (ThunarWindow *window)
 {
-  GtkAccelGroup *accel_group;
-  GtkWidget     *vbox;
-  GtkWidget     *menubar;
-  GtkWidget     *paned;
-  GtkWidget     *box;
+  GtkRadioAction *radio_action;
+  GtkAccelGroup  *accel_group;
+  GtkAction      *action;
+  GtkWidget      *vbox;
+  GtkWidget      *menubar;
+  GtkWidget      *paned;
+  GtkWidget      *box;
+  GSList         *group = NULL;
 
   window->action_group = gtk_action_group_new ("thunar-window");
   gtk_action_group_add_actions (window->action_group, action_entries,
                                 G_N_ELEMENTS (action_entries),
                                 GTK_WIDGET (window));
-  
+
+  /*
+   * add the location bar options
+   */
+  radio_action = gtk_radio_action_new ("view-location-bar-buttons", N_ ("_Button Style"), NULL, NULL, THUNAR_TYPE_LOCATION_BUTTONS);
+  gtk_action_group_add_action (window->action_group, GTK_ACTION (radio_action));
+  gtk_radio_action_set_group (radio_action, group);
+  group = gtk_radio_action_get_group (radio_action);
+  g_object_unref (G_OBJECT (radio_action));
+
+  radio_action = gtk_radio_action_new ("view-location-bar-entry", N_ ("_Traditional Style"), NULL, NULL, THUNAR_TYPE_LOCATION_ENTRY);
+  gtk_action_group_add_action (window->action_group, GTK_ACTION (radio_action));
+  gtk_radio_action_set_group (radio_action, group);
+  group = gtk_radio_action_get_group (radio_action);
+  g_object_unref (G_OBJECT (radio_action));
+
+  radio_action = gtk_radio_action_new ("view-location-bar-hidden", N_ ("_Hidden"), NULL, NULL, G_TYPE_NONE);
+  gtk_action_group_add_action (window->action_group, GTK_ACTION (radio_action));
+  gtk_radio_action_set_group (radio_action, group);
+  group = gtk_radio_action_get_group (radio_action);
+  g_object_unref (G_OBJECT (radio_action));
+
   window->ui_manager = gtk_ui_manager_new ();
   gtk_ui_manager_insert_action_group (window->ui_manager, window->action_group, 0);
   gtk_ui_manager_add_ui_from_string (window->ui_manager, thunar_window_ui, thunar_window_ui_length, NULL);
@@ -197,11 +230,7 @@ thunar_window_init (ThunarWindow *window)
   gtk_paned_pack2 (GTK_PANED (paned), box, TRUE, FALSE);
   gtk_widget_show (box);
 
-  window->location_bar = thunar_location_buttons_new ();
-  g_signal_connect_swapped (G_OBJECT (window->location_bar), "change-directory",
-                            G_CALLBACK (thunar_window_set_current_directory), window);
-  exo_binding_new (G_OBJECT (window), "current-directory",
-                   G_OBJECT (window->location_bar), "current-directory");
+  window->location_bar = gtk_alignment_new (0.0f, 0.5f, 1.0f, 1.0f);
   gtk_box_pack_start (GTK_BOX (box), window->location_bar, FALSE, FALSE, 0);
   gtk_widget_show (window->location_bar);
 
@@ -221,6 +250,11 @@ thunar_window_init (ThunarWindow *window)
                    G_OBJECT (window->statusbar), "text");
   gtk_box_pack_start (GTK_BOX (vbox), window->statusbar, FALSE, FALSE, 0);
   gtk_widget_show (window->statusbar);
+
+  /* activate the selected location bar */
+  action = gtk_action_group_get_action (window->action_group, "view-location-bar-buttons");
+  g_signal_connect (G_OBJECT (action), "changed", G_CALLBACK (thunar_window_action_location_bar_changed), window);
+  thunar_window_action_location_bar_changed (GTK_RADIO_ACTION (action), GTK_RADIO_ACTION (action), window);
 }
 
 
@@ -296,6 +330,36 @@ thunar_window_action_close (GtkAction    *action,
 
 
 
+static void
+thunar_window_action_location_bar_changed (GtkRadioAction *action,
+                                           GtkRadioAction *current,
+                                           ThunarWindow   *window)
+{
+  GtkWidget *widget;
+  GType      type;
+
+  /* drop the previous location bar (if any) */
+  widget = gtk_bin_get_child (GTK_BIN (window->location_bar));
+  if (G_LIKELY (widget != NULL))
+    gtk_widget_destroy (widget);
+
+  /* determine the new type of location bar */
+  type = gtk_radio_action_get_current_value (action);
+
+  if (G_LIKELY (type != G_TYPE_NONE))
+    {
+      widget = g_object_new (type, NULL);;
+      g_signal_connect_swapped (G_OBJECT (widget), "change-directory",
+                                G_CALLBACK (thunar_window_set_current_directory), window);
+      exo_binding_new (G_OBJECT (window), "current-directory",
+                       G_OBJECT (widget), "current-directory");
+      gtk_container_add (GTK_CONTAINER (window->location_bar), widget);
+      gtk_widget_show (widget);
+    }
+}
+
+
+
 static void
 thunar_window_action_go_up (GtkAction    *action,
                             ThunarWindow *window)
@@ -310,6 +374,32 @@ thunar_window_action_go_up (GtkAction    *action,
 
 
 
+static void
+thunar_window_action_location (GtkAction    *action,
+                               ThunarWindow *window)
+{
+  GtkWidget *dialog;
+  GtkWidget *widget;
+
+  /* bring up the "Open Location"-dialog if the window has no location bar or the location bar
+   * in the window does not support text entry by the user.
+   */
+  widget = gtk_bin_get_child (GTK_BIN (window->location_bar));
+  if (widget == NULL || !thunar_location_bar_accept_focus (THUNAR_LOCATION_BAR (widget)))
+    {
+      dialog = thunar_location_dialog_new ();
+      gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+      gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
+      gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
+      thunar_location_dialog_set_selected_file (THUNAR_LOCATION_DIALOG (dialog), thunar_window_get_current_directory (window));
+      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+        thunar_window_set_current_directory (window, thunar_location_dialog_get_selected_file (THUNAR_LOCATION_DIALOG (dialog)));
+      gtk_widget_destroy (dialog);
+    }
+}
+
+
+
 static void
 thunar_window_action_about (GtkAction    *action,
                             ThunarWindow *window)