From e5ff300ecb6b3fb6e00b5c4cece8bbdfbc03669c Mon Sep 17 00:00:00 2001
From: Benedikt Meurer <benny@xfce.org>
Date: Mon, 1 Aug 2005 22:36:21 +0000
Subject: [PATCH] 2005-08-02	Benedikt Meurer <benny@xfce.org>

	* configure.in.in: Add check for the FAM/Gamin library.
	* thunar-vfs/Makefile.am, thunar-vfs/thunar-vfs-monitor.{c,h}: Redesign
	  the VFS monitor to use FAM if available. It also provides an interface
	  to feed the monitor with external events, which will be used by the
	  VFS jobs, which know for sure that they changed/created/deleted a
	  file. The interface is not yet implemented.
	* thunar/thunar-file.{c,h}: Add a virtual method reload(), which allows
	  external entities to trigger a reload on a ThunarFile.
	* thunar/thunar-local-file.c: Implement the reload() method.
	* thunar/thunar-local-file.c, thunar/thunar-local-folder.c: Add support
	  for the new VFS monitor.




(Old svn revision: 16432)
---
 ChangeLog                       |  14 +
 configure.in.in                 |  71 +++-
 thunar-vfs/Makefile.am          |   6 +-
 thunar-vfs/thunar-vfs-monitor.c | 622 ++++++++++++++------------------
 thunar-vfs/thunar-vfs-monitor.h |  73 ++--
 thunar/thunar-file.c            |  32 ++
 thunar/thunar-file.h            |   4 +
 thunar/thunar-local-file.c      |  94 +++--
 thunar/thunar-local-folder.c    | 150 ++++++--
 9 files changed, 603 insertions(+), 463 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 665b0e37e..5428ab282 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2005-08-02	Benedikt Meurer <benny@xfce.org>
+
+	* configure.in.in: Add check for the FAM/Gamin library.
+	* thunar-vfs/Makefile.am, thunar-vfs/thunar-vfs-monitor.{c,h}: Redesign
+	  the VFS monitor to use FAM if available. It also provides an interface
+	  to feed the monitor with external events, which will be used by the
+	  VFS jobs, which know for sure that they changed/created/deleted a
+	  file. The interface is not yet implemented.
+	* thunar/thunar-file.{c,h}: Add a virtual method reload(), which allows
+	  external entities to trigger a reload on a ThunarFile.
+	* thunar/thunar-local-file.c: Implement the reload() method.
+	* thunar/thunar-local-file.c, thunar/thunar-local-folder.c: Add support
+	  for the new VFS monitor.
+
 2005-08-01	Benedikt Meurer <benny@xfce.org>
 
 	* thunar/thunar-list-model.{c,h}: Add new method
diff --git a/configure.in.in b/configure.in.in
index cdbf2cc72..5e08dcf3b 100644
--- a/configure.in.in
+++ b/configure.in.in
@@ -6,7 +6,9 @@ dnl
 dnl Written for Thunar by Benedikt Meurer <benny@xfce.org>.
 dnl
 
-dnl Version information
+dnl ***************************
+dnl *** Version information ***
+dnl ***************************
 m4_define([thunar_version_major], [0])
 m4_define([thunar_version_minor], [0])
 m4_define([thunar_version_micro], [2])
@@ -14,7 +16,9 @@ m4_define([thunar_version_build], [@REVISION@])
 m4_define([thunar_version_tag], [svn])
 m4_define([thunar_version], [thunar_version_major().thunar_version_minor().thunar_version_micro()ifelse(thunar_version_tag(), [], [], [thunar_version_tag()-thunar_version_build()])])
 
-dnl Initialize autoconf
+dnl ***************************
+dnl *** Initialize autoconf ***
+dnl ***************************
 AC_COPYRIGHT([Copyright (c) 2004-2005
         The Thunar development team. All rights reserved.
         
@@ -24,39 +28,78 @@ AC_PREREQ([2.50])
 AC_CANONICAL_TARGET()
 AC_REVISION([$Id$])
 
-dnl Initialize automake
+dnl ***************************
+dnl *** Initialize automake ***
+dnl ***************************
 AM_INIT_AUTOMAKE([AC_PACKAGE_TARNAME()], [AC_PACKAGE_VERSION()])
 AM_CONFIG_HEADER([config.h])
 AM_MAINTAINER_MODE()
 
-dnl check for UNIX variants
+dnl *******************************
+dnl *** Check for UNIX variants ***
+dnl *******************************
 AC_AIX()
 AC_ISC_POSIX()
 AC_MINIX()
 
-dnl check for basic programs
+dnl ********************************
+dnl *** Check for basic programs ***
+dnl ********************************
 AC_PROG_CC()
 AC_PROG_LD()
 AC_PROG_INSTALL()
 AC_PROG_LIBTOOL()
 
-dnl Check for standard headers
+dnl **********************************
+dnl *** Check for standard headers ***
+dnl **********************************
 AC_CHECK_HEADERS([dirent.h errno.h fcntl.h fstab.h grp.h locale.h \
                   math.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
+dnl ************************************
+dnl *** Check for standard functions ***
+dnl ************************************
 AC_CHECK_FUNCS([kqueue lchmod localtime_r readdir_r setgroupent setpassent])
 
-dnl Check for required packages
+dnl ***********************************
+dnl *** Check for required packages ***
+dnl ***********************************
 XDT_CHECK_PACKAGE([EXO], [exo-0.3], [0.3.1])
 XDT_CHECK_PACKAGE([GTHREAD], [gthread-2.0], [2.6.0])
 
-dnl Check for optional packages
+dnl ***********************************
+dnl *** Check for optional packages ***
+dnl ***********************************
 XDT_CHECK_OPTIONAL_PACKAGE([CAIRO], [cairo], [0.5], [cairo], [Cairo])
 
-dnl Check for the system flavour
+dnl *********************
+dnl *** Check for FAM ***
+dnl *********************
+LIBFAM_CFLAGS=""
+LIBFAM_LIBS=""
+AC_CHECK_HEADERS([fam.h],
+[
+  AC_CHECK_LIB([fam], [FAMOpen],
+  [
+    dnl Nice, we can use FAM
+    AC_DEFINE([HAVE_LIBFAM], [1], [Define if the File Alteration Monitor is available])
+    LIBFAM_LIBS="-lfam"
+
+    dnl Check for FAMNoExists (gamin only)
+    save_LIBS="$LIBS"
+    LIBS="$LIBS $LIBFAM_LIBS"
+    AC_CHECK_FUNCS([FAMNoExists])
+    LIBS="$save_LIBS"
+  ])
+])
+AC_SUBST([LIBFAM_CFLAGS])
+AC_SUBST([LIBFAM_LIBS])
+
+dnl ************************************
+dnl *** Check for the system flavour ***
+dnl ************************************
 AC_MSG_CHECKING([for system flavour])
 case "$target_os" in
 *bsd*)
@@ -68,14 +111,18 @@ case "$target_os" in
 esac
 AC_MSG_RESULT([$FLAVOUR])
 
-dnl Create links for the volume manager implementation
+dnl **********************************************************
+dnl *** Create links for the volume manager implementation ***
+dnl **********************************************************
 AC_CONFIG_LINKS(
 [
   thunar-vfs/thunar-vfs-volume-impl.c:thunar-vfs/thunar-vfs-volume-$FLAVOUR.c
   thunar-vfs/thunar-vfs-volume-impl.h:thunar-vfs/thunar-vfs-volume-$FLAVOUR.h
 ])
 
-dnl Check for debugging support
+dnl ***********************************
+dnl *** Check for debugging support ***
+dnl ***********************************
 AC_ARG_ENABLE([debug], AC_HELP_STRING([--disable-debug], [Disable debugging support]), [], [enable_debug=yes])
 AC_MSG_CHECKING([whether to enable debugging support])
 if test x"$enable_debug" != x"no"; then
diff --git a/thunar-vfs/Makefile.am b/thunar-vfs/Makefile.am
index 6e3fe0afc..30ce78fec 100644
--- a/thunar-vfs/Makefile.am
+++ b/thunar-vfs/Makefile.am
@@ -63,7 +63,8 @@ libthunar_vfs_la_SOURCES =						\
 
 libthunar_vfs_la_CFLAGS =						\
 	$(EXO_CFLAGS)							\
-	$(GTHREAD_CFLAGS)
+	$(GTHREAD_CFLAGS)						\
+	$(LIBFAM_CFLAGS)
 
 libthunar_vfs_la_LDFLAGS =						\
 	-no-undefined
@@ -74,7 +75,8 @@ libthunar_vfs_la_DEPENDENCIES =						\
 libthunar_vfs_la_LIBADD =						\
 	$(top_builddir)/thunar-vfs/xdgmime/libxdgmime.la		\
 	$(EXO_LIBS)							\
-	$(GTHREADS_LIBS)
+	$(GTHREADS_LIBS)						\
+	$(LIBFAM_LIBS)
 
 EXTRA_DIST =								\
 	thunar-vfs-marshal.list						\
diff --git a/thunar-vfs/thunar-vfs-monitor.c b/thunar-vfs/thunar-vfs-monitor.c
index 0fe3b2e6d..1cf340394 100644
--- a/thunar-vfs/thunar-vfs-monitor.c
+++ b/thunar-vfs/thunar-vfs-monitor.c
@@ -22,47 +22,27 @@
 #include <config.h>
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_EVENT_H
-#include <sys/event.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
+#ifdef HAVE_FAM_H
+#include <fam.h>
 #endif
 
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
+#include <gdk/gdk.h>
 
 #include <thunar-vfs/thunar-vfs-monitor.h>
 
 
 
-/* the monitor timer interval */
-#define THUNAR_VFS_MONITOR_INTERVAL (4 * 1000)
-
-
-
-typedef struct _ThunarVfsWatch ThunarVfsWatch;
-
-
-
-static void     thunar_vfs_monitor_class_init     (ThunarVfsMonitorClass *klass);
-static void     thunar_vfs_monitor_init           (ThunarVfsMonitor      *monitor);
-static void     thunar_vfs_monitor_finalize       (GObject               *object);
-static gboolean thunar_vfs_monitor_event          (GIOChannel            *source,
-                                                   GIOCondition           condition,
-                                                   gpointer               user_data);
-static gboolean thunar_vfs_monitor_timer          (gpointer               user_data);
-static void     thunar_vfs_monitor_timer_destroy  (gpointer               user_data);
+static void     thunar_vfs_monitor_class_init (ThunarVfsMonitorClass *klass);
+static void     thunar_vfs_monitor_init       (ThunarVfsMonitor      *monitor);
+static void     thunar_vfs_monitor_finalize   (GObject               *object);
+#ifdef HAVE_LIBFAM
+static void     thunar_vfs_monitor_fam_cancel (ThunarVfsMonitor      *monitor);
+static void     thunar_vfs_monitor_fam_event  (ThunarVfsMonitor      *monitor,
+                                               const FAMEvent        *fe);
+static gboolean thunar_vfs_monitor_fam_watch (GIOChannel             *channel,
+                                              GIOCondition            condition,
+                                              gpointer                user_data);
+#endif
 
 
 
@@ -75,21 +55,25 @@ struct _ThunarVfsMonitor
 {
   GObject __parent__;
 
-  GList *watches;
-  gint   current_id;
-  gint   timer_id;
+  GMemChunk    *handle_chunk;
+  GList        *handles;
 
-  gint   kq;
-  gint   kq_watch_id;
+#ifdef HAVE_LIBFAM
+  FAMConnection fc;
+  gint          fc_watch_id;
+  gint          fc_sequence;
+#endif
 };
 
-struct _ThunarVfsWatch
+struct _ThunarVfsMonitorHandle
 {
-  gint                     id;
-  gint                     fd;
-  ThunarVfsInfo           *info;
   ThunarVfsMonitorCallback callback;
   gpointer                 user_data;
+  ThunarVfsURI            *uri;
+
+#ifdef HAVE_LIBFAM
+  FAMRequest               fr;
+#endif
 };
 
 
@@ -112,35 +96,29 @@ thunar_vfs_monitor_class_init (ThunarVfsMonitorClass *klass)
 static void
 thunar_vfs_monitor_init (ThunarVfsMonitor *monitor)
 {
-  GIOChannel *channel;
-
-  monitor->current_id = 0;
-  monitor->timer_id = g_timeout_add_full (G_PRIORITY_LOW, THUNAR_VFS_MONITOR_INTERVAL,
-                                          thunar_vfs_monitor_timer, monitor,
-                                          thunar_vfs_monitor_timer_destroy);
-
-  /* open a connection to the system's event queue */
-#ifdef HAVE_KQUEUE
-  monitor->kq = kqueue ();
-#else
-  monitor->kq = -1;
-#endif
-
-  if (G_LIKELY (monitor->kq >= 0))
+#ifdef HAVE_LIBFAM
+  if (FAMOpen2 (&monitor->fc, PACKAGE_NAME) == 0)
     {
-      /* make sure the connection is not passed on to child processes */
-      fcntl (monitor->kq, F_SETFD, fcntl (monitor->kq, F_GETFD, 0) | FD_CLOEXEC);
+      GIOChannel *channel;
 
-      /* setup an io channel for the connection */
-      channel = g_io_channel_unix_new (monitor->kq);
-      monitor->kq_watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP | G_IO_IN,
-                                             thunar_vfs_monitor_event, monitor);
+      channel = g_io_channel_unix_new (FAMCONNECTION_GETFD (&monitor->fc));
+      monitor->fc_watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP | G_IO_IN,
+                                             thunar_vfs_monitor_fam_watch, monitor);
       g_io_channel_unref (channel);
+
+#ifdef HAVE_FAMNOEXISTS
+      /* luckily gamin offers a way to avoid the FAMExists events */
+      FAMNoExists (&monitor->fc);
+#endif
     }
   else
     {
-      monitor->kq_watch_id = -1;
+      monitor->fc_watch_id = -1;
     }
+#endif
+
+  /* allocate the memory chunk for the handles */
+  monitor->handle_chunk = g_mem_chunk_create (ThunarVfsMonitorHandle, 64, G_ALLOC_AND_FREE);
 }
 
 
@@ -149,240 +127,158 @@ static void
 thunar_vfs_monitor_finalize (GObject *object)
 {
   ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (object);
-  ThunarVfsWatch   *watch;
   GList            *lp;
 
-  /* drop the still present (previously deleted) watches */
-  for (lp = monitor->watches; lp != NULL; lp = lp->next)
-    {
-      watch = lp->data;
-      if (G_UNLIKELY (watch->callback != NULL))
-        {
-          g_error ("Tried to finalize a ThunarVfsMonitor, which has "
-                   "atleast one active watch monitoring \"%s\"",
-                   thunar_vfs_uri_to_string (watch->info->uri, 0));
-        }
-
-#ifdef HAVE_KQUEUE
-      if (G_LIKELY (watch->fd >= 0))
-        {
-          /* prepare the delete command */
-          struct kevent ev;
-          ev.ident = watch->fd;
-          ev.filter = EVFILT_VNODE;
-          ev.flags = EV_DELETE;
-          ev.fflags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE;
-
-          /* perform the delete operation */
-          if (kevent (monitor->kq, &ev, 1, NULL, 0, NULL) < 0)
-            g_error ("Failed to remove watch %d: %s", watch->id, g_strerror (errno));
-
-          /* drop the file descriptor */
-          close (watch->fd);
-        }
+#ifdef HAVE_LIBFAM
+  if (monitor->fc_watch_id >= 0)
+    thunar_vfs_monitor_fam_cancel (monitor);
 #endif
 
-      g_free (watch);
-    }
-  g_list_free (monitor->watches);
+  /* drop all handles */
+  for (lp = monitor->handles; lp != NULL; lp = lp->next)
+    thunar_vfs_uri_unref (((ThunarVfsMonitorHandle *) lp->data)->uri);
+  g_list_free (monitor->handles);
+
+  /* release the memory chunk */
+  g_mem_chunk_destroy (monitor->handle_chunk);
+
+  (*G_OBJECT_CLASS (thunar_vfs_monitor_parent_class)->finalize) (object);
+}
+
 
-  /* stop the timer */
-  if (G_LIKELY (monitor->timer_id >= 0))
-    g_source_remove (monitor->timer_id);
 
-  /* remove the main loop watch for the event loop */
-  if (G_LIKELY (monitor->kq_watch_id >= 0))
-    g_source_remove (monitor->kq_watch_id);
+#ifdef HAVE_LIBFAM
+static void
+thunar_vfs_monitor_fam_cancel (ThunarVfsMonitor *monitor)
+{
+  g_return_if_fail (THUNAR_VFS_IS_MONITOR (monitor));
+  g_return_if_fail (monitor->fc_watch_id >= 0);
 
-  /* destroy the event queue */
-  if (G_LIKELY (monitor->kq >= 0))
-    close (monitor->kq);
+  /* close the FAM connection */
+  FAMClose (&monitor->fc);
 
-  G_OBJECT_CLASS (thunar_vfs_monitor_parent_class)->finalize (object);
+  /* remove the I/O watch */
+  g_source_remove (monitor->fc_watch_id);
+  monitor->fc_watch_id = -1;
 }
 
 
 
-static gboolean
-thunar_vfs_monitor_event (GIOChannel  *source,
-                          GIOCondition condition,
-                          gpointer     user_data)
+static void
+thunar_vfs_monitor_fam_event (ThunarVfsMonitor *monitor,
+                              const FAMEvent   *fe)
 {
-#if 0
-#ifdef HAVE_KQUEUE
-  ThunarVfsInfoResult result;
-  ThunarVfsMonitor   *monitor = THUNAR_VFS_MONITOR (user_data);
-  ThunarVfsWatch     *watch;
-  struct kevent       events[16];
-  gint                n;
-
-  GDK_THREADS_ENTER ();
-
-  n = kevent (monitor->kq, NULL, 0, events, G_N_ELEMENTS (events), NULL);
-  while (--n >= 0)
+  ThunarVfsMonitorHandle *handle;
+  ThunarVfsMonitorEvent   event;
+  ThunarVfsURI           *uri;
+  GList                  *lp;
+
+  /* lookup the handle for the request that caused the event */
+  for (lp = monitor->handles; lp != NULL; lp = lp->next)
+    if (((ThunarVfsMonitorHandle *) lp->data)->fr.reqnum == fe->fr.reqnum)
+      break;
+
+  /* yes, this can really happen, see
+   * the FAM documentation.
+   */
+  if (G_UNLIKELY (lp == NULL))
+    return;
+  else
+    handle = lp->data;
+
+  /* translate the event code */
+  switch (fe->code)
     {
-      watch = events[n].udata;
-      if (G_UNLIKELY (watch->callback == NULL))
-        continue;
+    case FAMChanged:
+      event = THUNAR_VFS_MONITOR_EVENT_CHANGED;
+      break;
+
+    case FAMCreated:
+      event = THUNAR_VFS_MONITOR_EVENT_CREATED;
+      break;
 
-      /* force an update on the info */
-      result = 0; /*thunar_vfs_info_update (watch->info, NULL);*/
-      switch (result)
-        {
-        case THUNAR_VFS_INFO_RESULT_ERROR:
-          watch->callback (monitor, THUNAR_VFS_MONITOR_DELETED, watch->info, watch->user_data);
-          break;
-
-        case THUNAR_VFS_INFO_RESULT_CHANGED:
-          watch->callback (monitor, THUNAR_VFS_MONITOR_CHANGED, watch->info, watch->user_data);
-          break;
-        
-        case THUNAR_VFS_INFO_RESULT_NOCHANGE:
-          break;
-
-        default:
-          g_assert_not_reached ();
-          break;
-        }
+    case FAMDeleted:
+      event = THUNAR_VFS_MONITOR_EVENT_DELETED;
+      break;
+
+    default:
+      g_assert_not_reached ();
+      return;
     }
 
-  GDK_THREADS_LEAVE ();
-#endif
-#endif
+  /* determine the URI */
+  if (G_UNLIKELY (*fe->filename != '/'))
+    uri = thunar_vfs_uri_relative (handle->uri, fe->filename);
+  else if (strcmp (thunar_vfs_uri_get_path (handle->uri), fe->filename) == 0)
+    uri = thunar_vfs_uri_ref (handle->uri);
+  else
+    uri = thunar_vfs_uri_new_for_path (fe->filename);
 
-  /* keep the kqueue monitor going */
-  return TRUE;
+  /* invoke the callback, fortunately - due to our design - we
+   * have no reentrancy issues here. ;-)
+   */
+  (*handle->callback) (monitor, handle, event, handle->uri, uri, handle->user_data);
+  thunar_vfs_uri_unref (uri);
 }
 
 
 
 static gboolean
-thunar_vfs_monitor_timer (gpointer user_data)
+thunar_vfs_monitor_fam_watch (GIOChannel  *channel,
+                              GIOCondition condition,
+                              gpointer     user_data)
 {
-#if 0
-  ThunarVfsInfoResult result;
-  ThunarVfsMonitor   *monitor = THUNAR_VFS_MONITOR (user_data);
-  ThunarVfsWatch     *watch;
-  GList              *changed = NULL;
-  GList              *deleted = NULL;
-  GList              *next;
-  GList              *lp;
-
-  GDK_THREADS_ENTER ();
-
-  for (lp = monitor->watches; lp != NULL; lp = next)
-    {
-      watch = lp->data;
-      next = lp->next;
-
-      /* handle deletion of watches */
-      if (G_UNLIKELY (watch->callback == NULL))
-        {
-#ifdef HAVE_KQUEUE
-          if (G_LIKELY (watch->fd >= 0))
-            {
-              /* prepare the delete command */
-              struct kevent ev;
-              ev.ident = watch->fd;
-              ev.filter = EVFILT_VNODE;
-              ev.flags = EV_DELETE;
-              ev.fflags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE;
-
-              /* perform the delete operation */
-              if (kevent (monitor->kq, &ev, 1, NULL, 0, NULL) < 0)
-                g_error ("Failed to remove watch %d: %s", watch->id, g_strerror (errno));
-
-              /* drop the file descriptor */
-              close (watch->fd);
-            }
-#endif
-
-          monitor->watches = g_list_delete_link (monitor->watches, lp);
-          g_free (watch);
-          continue;
-        }
-
-#ifdef HAVE_KQUEUE
-      if (G_LIKELY (watch->fd >= 0))
-        continue;
-#endif
+  ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (user_data);
+  FAMEvent          fe;
 
-      /* force an update on the info */
-      result = thunar_vfs_info_update (watch->info, NULL);
-      switch (result)
-        {
-        case THUNAR_VFS_INFO_RESULT_ERROR:
-          deleted = g_list_prepend (deleted, watch);
-          break;
-
-        case THUNAR_VFS_INFO_RESULT_CHANGED:
-          changed = g_list_prepend (changed, watch);
-          break;
-        
-        case THUNAR_VFS_INFO_RESULT_NOCHANGE:
-          break;
-
-        default:
-          g_assert_not_reached ();
-          break;
-        }
-    }
-
-  /* notify all files that have been changed */
-  if (G_UNLIKELY (changed != NULL))
+  /* check for an error on the FAM connection */
+  if (G_UNLIKELY ((condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) != 0))
     {
-      for (lp = changed; lp != NULL; lp = lp->next)
-        {
-          watch = lp->data;
-          if (G_LIKELY (watch->callback != NULL))
-            watch->callback (monitor, THUNAR_VFS_MONITOR_CHANGED, watch->info, watch->user_data);
-        }
-      g_list_free (changed);
+error:
+      /* terminate the FAM connection */
+      GDK_THREADS_ENTER ();
+      thunar_vfs_monitor_fam_cancel (monitor);
+      GDK_THREADS_LEAVE ();
+
+      /* thats it, no more FAM */
+      return FALSE;
     }
 
-  /* notify all files that have been deleted */
-  if (G_UNLIKELY (deleted != NULL))
+  /* process all pending FAM events */
+  while (FAMPending (&monitor->fc))
     {
-      for (lp = deleted; lp != NULL; lp = lp->next)
-        {
-          watch = lp->data;
-          if (G_LIKELY (watch->callback != NULL))
-            watch->callback (monitor, THUNAR_VFS_MONITOR_DELETED, watch->info, watch->user_data);
-        }
-      g_list_free (deleted);
-    }
-
-  GDK_THREADS_LEAVE ();
-#endif
-  
-  return TRUE;
-}
+      /* query the next pending event */
+      if (G_UNLIKELY (FAMNextEvent (&monitor->fc, &fe) < 0))
+        goto error;
 
+      /* we ignore all events other than changed/created/deleted */
+      if (G_UNLIKELY (fe.code != FAMChanged && fe.code != FAMCreated && fe.code != FAMDeleted))
+        continue;
 
+      /* process the event */
+      GDK_THREADS_ENTER ();
+      thunar_vfs_monitor_fam_event (monitor, &fe);
+      GDK_THREADS_LEAVE ();
+    }
 
-static void
-thunar_vfs_monitor_timer_destroy (gpointer user_data)
-{
-  GDK_THREADS_ENTER ();
-  THUNAR_VFS_MONITOR (user_data)->timer_id = -1;
-  GDK_THREADS_LEAVE ();
+  return TRUE;
 }
+#endif
 
 
 
 /**
- * thunar_vfs_monitor_get_default:
+ * thunar_vfs_monitor_get:
  *
- * Returns the default #ThunarVfsMonitor which is shared
- * by all modules.
+ * Returns the shared #ThunarVfsMonitor instance. The caller
+ * is responsible to call #g_object_unref() on the returned
+ * object when no longer needed.
  *
- * You need to call #g_object_unref() on the returned
- * object when you don't need the monitor any longer.
- *
- * Return value: the default #ThunarVfsMonitor instance.
+ * Return value: a reference to the shared #ThunarVfsMonitor
+ *               instance.
  **/
 ThunarVfsMonitor*
-thunar_vfs_monitor_get_default (void)
+thunar_vfs_monitor_get (void)
 {
   static ThunarVfsMonitor *monitor = NULL;
 
@@ -402,144 +298,150 @@ thunar_vfs_monitor_get_default (void)
 
 
 /**
- * thunar_vfs_monitor_add_info:
+ * thunar_vfs_monitor_add_directory:
  * @monitor   : a #ThunarVfsMonitor.
- * @info      : pointer to the #ThunarVfsInfo object to watch.
- * @callback  : callback function to invoke whenever changes to the file are detected.
- * @user_data : additional user specific data to pass to @callback.
- *
- * Adds @info to the list of watched entities of @monitor. Note that this
- * way of monitoring is very special and optimized for very low overhead.
- * No copy of @info is made, instead the #ThunarVfsInfo object referenced
- * by @info is modified directly whenever a change is noticed to avoid
- * an additional #stat() invokation. This means, that you'll need to
- * unregister the watch BEFORE freeing @info!
+ * @uri       : the #ThunarVfsURI of the directory that should be watched.
+ * @callback  : the callback function to invoke.
+ * @user_data : additional data to pass to @callback.
  *
- * Returns the unique id of the newly registered watch, which can later
- * be used to drop the watch from the @monitor using the method
- * #thunar_vfs_monitor_remove().
+ * The @uri is currently limited to the #THUNAR_VFS_URI_SCHEME_FILE.
  *
- * Return value: the unique id of the newly created watch.
+ * Return value: the #ThunarVfsMonitorHandle for the new watch.
  **/
-gint
-thunar_vfs_monitor_add_info (ThunarVfsMonitor        *monitor,
-                             ThunarVfsInfo           *info,
-                             ThunarVfsMonitorCallback callback,
-                             gpointer                 user_data)
+ThunarVfsMonitorHandle*
+thunar_vfs_monitor_add_directory (ThunarVfsMonitor        *monitor,
+                                  ThunarVfsURI            *uri,
+                                  ThunarVfsMonitorCallback callback,
+                                  gpointer                 user_data)
 {
-  ThunarVfsWatch *watch;
-
-  g_return_val_if_fail (THUNAR_VFS_IS_MONITOR (monitor), 0);
-  g_return_val_if_fail (info != NULL && THUNAR_VFS_IS_URI (info->uri), 0);
-  g_return_val_if_fail (callback != NULL, 0);
+  ThunarVfsMonitorHandle *handle;
 
-  /* prepare the watch */
-  watch = g_new (ThunarVfsWatch, 1);
-  watch->id = ++monitor->current_id;
-  watch->fd = -1;
-  watch->info = info;
-  watch->callback = callback;
-  watch->user_data = user_data;
+  g_return_val_if_fail (THUNAR_VFS_IS_MONITOR (monitor), NULL);
+  g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
+  g_return_val_if_fail (thunar_vfs_uri_get_scheme (uri) == THUNAR_VFS_URI_SCHEME_FILE, NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
 
-  /* add it to the list of active watches */
-  monitor->watches = g_list_prepend (monitor->watches, watch);
+  /* allocate a new handle */
+  handle = g_chunk_new (ThunarVfsMonitorHandle, monitor->handle_chunk);
+  handle->uri = thunar_vfs_uri_ref (uri);
+  handle->callback = callback;
+  handle->user_data = user_data;
 
-  /* For now, symlinks will be checked regularly using the timer */
-  if ((info->flags & THUNAR_VFS_FILE_FLAGS_SYMLINK) == 0)
+#ifdef HAVE_LIBFAM
+  if (G_LIKELY (monitor->fc_watch_id >= 0))
     {
-#ifdef HAVE_KQUEUE
-      if (G_LIKELY (monitor->kq >= 0))
-        {
-          watch->fd = open (thunar_vfs_uri_get_path (info->uri), O_RDONLY, 0);
-          if (watch->fd >= 0)
-            {
-              /* prepare the event definition */
-              struct kevent ev;
-              ev.ident = watch->fd;
-              ev.filter = EVFILT_VNODE;
-              ev.flags = EV_ADD | EV_ENABLE | EV_CLEAR;
-              ev.fflags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE;
-              ev.udata = watch;
-
-              /* try to add the event */
-              if (kevent (monitor->kq, &ev, 1, NULL, 0, NULL) < 0)
-                {
-                  close (watch->fd);
-                  watch->fd = -1;
-                }
-            }
-        }
-#endif
+      /* generate a unique sequence number for the request */
+      handle->fr.reqnum = ++monitor->fc_sequence;
+
+      /* schedule the watch on the FAM daemon */
+      if (FAMMonitorDirectory2 (&monitor->fc, thunar_vfs_uri_get_path (uri), &handle->fr) < 0)
+        thunar_vfs_monitor_fam_cancel (monitor);
     }
+#endif
+
+  /* add the handle to the monitor */
+  monitor->handles = g_list_prepend (monitor->handles, handle);
 
-  return watch->id;
+  return handle;
 }
 
 
 
 /**
- * thunar_vfs_monitor_add_path:
+ * thunar_vfs_monitor_add_file:
  * @monitor   : a #ThunarVfsMonitor.
- * @path      : the path to the file or directory to monitor for changes.
- * @callback  : a callback function to be invoked whenever changes are noticed on the @path.
- * @user_data : additional user specific data to pass to @callback.
+ * @uri       : the #ThunarVfsURI of the file that should be watched.
+ * @callback  : the callback function to invoke.
+ * @user_data : additional data to pass to @callback.
  *
- * FIXME
+ * The @uri is currently limited to the #THUNAR_VFS_URI_SCHEME_FILE.
  *
- * Returns the unique id of the newly registered watch, which can later
- * be used to drop the watch from the @monitor using the method
- * #thunar_vfs_monitor_remove().
- *
- * Return value: the watch id.
+ * Return value: the #ThunarVfsMonitorHandle for the new watch.
  **/
-gint
-thunar_vfs_monitor_add_path (ThunarVfsMonitor        *monitor,
-                             const gchar             *path,
+ThunarVfsMonitorHandle*
+thunar_vfs_monitor_add_file (ThunarVfsMonitor        *monitor,
+                             ThunarVfsURI            *uri,
                              ThunarVfsMonitorCallback callback,
                              gpointer                 user_data)
 {
-  g_return_val_if_fail (THUNAR_VFS_IS_MONITOR (monitor), 0);
-  g_return_val_if_fail (g_path_is_absolute (path), 0);
-  g_return_val_if_fail (callback != NULL, 0);
+  ThunarVfsMonitorHandle *handle;
+
+  g_return_val_if_fail (THUNAR_VFS_IS_MONITOR (monitor), NULL);
+  g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
+  g_return_val_if_fail (thunar_vfs_uri_get_scheme (uri) == THUNAR_VFS_URI_SCHEME_FILE, NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
 
-  g_error ("Not implemented yet!");
+  /* allocate a new handle */
+  handle = g_chunk_new (ThunarVfsMonitorHandle, monitor->handle_chunk);
+  handle->uri = thunar_vfs_uri_ref (uri);
+  handle->callback = callback;
+  handle->user_data = user_data;
 
-  return 0;
+#ifdef HAVE_LIBFAM
+  if (G_LIKELY (monitor->fc_watch_id >= 0))
+    {
+      /* generate a unique sequence number for the request */
+      handle->fr.reqnum = ++monitor->fc_sequence;
+
+      /* schedule the watch on the FAM daemon */
+      if (FAMMonitorFile2 (&monitor->fc, thunar_vfs_uri_get_path (uri), &handle->fr) < 0)
+        thunar_vfs_monitor_fam_cancel (monitor);
+    }
+#endif
+
+  /* add the handle to the monitor */
+  monitor->handles = g_list_prepend (monitor->handles, handle);
+
+  return handle;
 }
 
 
 
 /**
- * thunar_vfs_monitor_remove_info:
+ * thunar_vfs_monitor_remove:
  * @monitor : a #ThunarVfsMonitor.
- * @id      : the watch id.
- *
- * Removes an existing watch identified by @id from the
- * given @monitor.
+ * @handle  : a valid #ThunarVfsMonitorHandle for @monitor.
  *
- * Note that the @id must be valid, else the application
- * will abort.
+ * Removes @handle from @monitor.
  **/
 void
-thunar_vfs_monitor_remove (ThunarVfsMonitor *monitor,
-                           gint              id)
+thunar_vfs_monitor_remove (ThunarVfsMonitor       *monitor,
+                           ThunarVfsMonitorHandle *handle)
 {
-  ThunarVfsWatch *watch;
-  GList          *lp;
-
   g_return_if_fail (THUNAR_VFS_IS_MONITOR (monitor));
-  g_return_if_fail (id > 0);
+  g_return_if_fail (g_list_find (monitor->handles, handle) != NULL);
 
-  for (lp = monitor->watches; lp != NULL; lp = lp->next)
-    {
-      watch = lp->data;
-      if (watch->id == id && watch->callback != NULL)
-        {
-          /* mark this watch for deletion */
-          watch->callback = NULL;
-          return;
-        }
-    }
+  /* unlink the handle */
+  monitor->handles = g_list_remove (monitor->handles, handle);
 
-  g_error ("Watch %d not present in ThunarVfsMonitor", id);
+#ifdef HAVE_LIBFAM
+  if (G_LIKELY (monitor->fc_watch_id >= 0 && handle->fr.reqnum > 0))
+    if (FAMCancelMonitor (&monitor->fc, &handle->fr) < 0)
+      thunar_vfs_monitor_fam_cancel (monitor);
+#endif
+
+  /* free the handle */
+  thunar_vfs_uri_unref (handle->uri);
+  g_mem_chunk_free (monitor->handle_chunk, handle);
 }
+
+
+
+/**
+ * thunar_vfs_monitor_feed:
+ * @monitor
+ * @event
+ * @uri
+ **/
+void
+thunar_vfs_monitor_feed (ThunarVfsMonitor     *monitor,
+                         ThunarVfsMonitorEvent event,
+                         ThunarVfsURI         *uri)
+{
+  // FIXME
+  g_assert_not_reached ();
+}
+
+
+
+
diff --git a/thunar-vfs/thunar-vfs-monitor.h b/thunar-vfs/thunar-vfs-monitor.h
index e12290c12..cf28f2f6f 100644
--- a/thunar-vfs/thunar-vfs-monitor.h
+++ b/thunar-vfs/thunar-vfs-monitor.h
@@ -21,7 +21,7 @@
 #ifndef __THUNAR_VFS_MONITOR_H__
 #define __THUNAR_VFS_MONITOR_H__
 
-#include <thunar-vfs/thunar-vfs-info.h>
+#include <thunar-vfs/thunar-vfs-uri.h>
 
 G_BEGIN_DECLS;
 
@@ -37,42 +37,67 @@ typedef struct _ThunarVfsMonitor      ThunarVfsMonitor;
 
 /**
  * ThunarVfsMonitorEvent:
- * @THUNAR_VFS_MONITOR_CHANGED: a monitored file has changed.
- * @THUNAR_VFS_MONITOR_DELETED: a monitored file was deleted.
- * FIXME
+ * @THUNAR_VFS_MONITOR_EVENT_CHANGED : a file or directory was changed.
+ * @THUNAR_VFS_MONITOR_EVENT_CREATED : a file or directory was created.
+ * @THUNAR_VFS_MONITOR_EVENT_DELETED : a file or directory was deleted.
+ *
+ * Describes an event that occurred on a #ThunarVfsMonitorHandle.
  **/
 typedef enum
 {
-  THUNAR_VFS_MONITOR_CHANGED,
-  THUNAR_VFS_MONITOR_CREATED,
-  THUNAR_VFS_MONITOR_DELETED,
+  THUNAR_VFS_MONITOR_EVENT_CHANGED,
+  THUNAR_VFS_MONITOR_EVENT_CREATED,
+  THUNAR_VFS_MONITOR_EVENT_DELETED,
 } ThunarVfsMonitorEvent;
 
+/**
+ * ThunarVfsMonitorHandle:
+ *
+ * A handle on a file system entity, which is currently watched
+ * by a #ThunarVfsMonitor.
+ **/
+typedef struct _ThunarVfsMonitorHandle ThunarVfsMonitorHandle;
+
 /**
  * ThunarVfsMonitorCallback:
- * FIXME
+ * @monitor    : a #ThunarVfsMonitor.
+ * @handle     : a #ThunarVfsMonitorHandle.
+ * @event      : the event that occurred.
+ * @handle_uri : the #ThunarVfsURI that was specified when registering the @handle.
+ * @event_uri  : the #ThunarVfsURI on which the @event occurred.
+ * @user_data  : the user data that was specified when registering the @handle with the @monitor.
+ *
+ * The prototype for callback functions that will be called by a #ThunarVfsMonitor
+ * whenever one of its associated #ThunarVfsMonitorHandle<!---->s notice a
+ * change.
  **/
-typedef void (*ThunarVfsMonitorCallback) (ThunarVfsMonitor     *monitor,
-                                          ThunarVfsMonitorEvent event,
-                                          ThunarVfsInfo        *info,
-                                          gpointer              user_data);
+typedef void (*ThunarVfsMonitorCallback)  (ThunarVfsMonitor       *monitor,
+                                           ThunarVfsMonitorHandle *handle,
+                                           ThunarVfsMonitorEvent   event,
+                                           ThunarVfsURI           *handle_uri,
+                                           ThunarVfsURI           *event_uri,
+                                           gpointer                user_data);
+
+GType                   thunar_vfs_monitor_get_type       (void) G_GNUC_CONST;
 
-GType             thunar_vfs_monitor_get_type     (void) G_GNUC_CONST;
+ThunarVfsMonitor       *thunar_vfs_monitor_get            (void);
 
-ThunarVfsMonitor *thunar_vfs_monitor_get_default  (void);
+ThunarVfsMonitorHandle *thunar_vfs_monitor_add_directory  (ThunarVfsMonitor        *monitor,
+                                                           ThunarVfsURI            *uri,
+                                                           ThunarVfsMonitorCallback callback,
+                                                           gpointer                 user_data);
 
-gint              thunar_vfs_monitor_add_info     (ThunarVfsMonitor        *monitor,
-                                                   ThunarVfsInfo           *info,
-                                                   ThunarVfsMonitorCallback callback,
-                                                   gpointer                 user_data);
+ThunarVfsMonitorHandle *thunar_vfs_monitor_add_file       (ThunarVfsMonitor        *monitor,
+                                                           ThunarVfsURI            *uri,
+                                                           ThunarVfsMonitorCallback callback,
+                                                           gpointer                 user_data);
 
-gint              thunar_vfs_monitor_add_path     (ThunarVfsMonitor        *monitor,
-                                                   const gchar             *path,
-                                                   ThunarVfsMonitorCallback callback,
-                                                   gpointer                 user_data);
+void                    thunar_vfs_monitor_remove         (ThunarVfsMonitor        *monitor,
+                                                           ThunarVfsMonitorHandle  *handle);
 
-void              thunar_vfs_monitor_remove       (ThunarVfsMonitor        *monitor,
-                                                   gint                     id);
+void                    thunar_vfs_monitor_feed           (ThunarVfsMonitor        *monitor,
+                                                           ThunarVfsMonitorEvent    event,
+                                                           ThunarVfsURI            *uri);
 
 G_END_DECLS;
 
diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c
index 031223918..ab5d46e02 100644
--- a/thunar/thunar-file.c
+++ b/thunar/thunar-file.c
@@ -65,6 +65,7 @@ static gboolean           thunar_file_real_can_execute         (ThunarFile
 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_reload              (ThunarFile             *file);
 static void               thunar_file_real_changed             (ThunarFile             *file);
 static ThunarFile        *thunar_file_new_internal             (ThunarVfsURI           *uri,
                                                                 GError                **error);
@@ -178,6 +179,7 @@ thunar_file_class_init (ThunarFileClass *klass)
   klass->can_read = thunar_file_real_can_read;
   klass->can_write = thunar_file_real_can_write;
   klass->get_emblem_names = thunar_file_real_get_emblem_names;
+  klass->reload = thunar_file_real_reload;
   klass->changed = thunar_file_real_changed;
 
   /**
@@ -369,6 +371,14 @@ thunar_file_real_get_emblem_names (ThunarFile *file)
 
 
 
+static void
+thunar_file_real_reload (ThunarFile *file)
+{
+  /* we don't do anything here... */
+}
+
+
+
 static void
 thunar_file_real_changed (ThunarFile *file)
 {
@@ -1242,6 +1252,28 @@ thunar_file_unwatch (ThunarFile *file)
 }
 
 
+
+/**
+ * thunar_file_reload:
+ * @file : a #ThunarFile instance.
+ *
+ * Tells @file to reload its internal state, e.g. by reacquiring
+ * the file info from the underlying media. Not all #ThunarFile
+ * implementations may actually implement this method, so don't
+ * count on it to do anything useful. Some implementations may
+ * also decide to reload its state asynchronously.
+ *
+ * You must also be able to handle the case that @file is
+ * destroyed during the reload call.
+ **/
+void
+thunar_file_reload (ThunarFile *file)
+{
+  g_return_if_fail (THUNAR_IS_FILE (file));
+  THUNAR_FILE_GET_CLASS (file)->reload (file);
+}
+
+
  
 /**
  * thunar_file_changed:
diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h
index f4fb86d27..939805366 100644
--- a/thunar/thunar-file.h
+++ b/thunar/thunar-file.h
@@ -105,6 +105,8 @@ struct _ThunarFileClass
   void                 (*watch)               (ThunarFile             *file);
   void                 (*unwatch)             (ThunarFile             *file);
 
+  void                 (*reload)              (ThunarFile             *file);
+
 
   /* signals */
   void (*changed) (ThunarFile *file);
@@ -173,6 +175,8 @@ GdkPixbuf         *thunar_file_load_icon        (ThunarFile             *file,
 void               thunar_file_watch            (ThunarFile             *file);
 void               thunar_file_unwatch          (ThunarFile             *file);
 
+void               thunar_file_reload           (ThunarFile             *file);
+
 void               thunar_file_changed          (ThunarFile             *file);
 
 gboolean           thunar_file_is_hidden        (ThunarFile             *file);
diff --git a/thunar/thunar-local-file.c b/thunar/thunar-local-file.c
index 88e0f02a3..b648396f6 100644
--- a/thunar/thunar-local-file.c
+++ b/thunar/thunar-local-file.c
@@ -53,9 +53,12 @@ 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_reload            (ThunarFile             *file);
 static void               thunar_local_file_monitor           (ThunarVfsMonitor       *monitor,
+                                                               ThunarVfsMonitorHandle *handle,
                                                                ThunarVfsMonitorEvent   event,
-                                                               ThunarVfsInfo          *info,
+                                                               ThunarVfsURI           *handle_uri,
+                                                               ThunarVfsURI           *event_uri,
                                                                gpointer                user_data);
 
 
@@ -71,10 +74,10 @@ struct _ThunarLocalFile
 {
   ThunarFile __parent__;
 
-  gchar         *display_name;
-  ThunarVfsInfo *info;
+  gchar                  *display_name;
+  ThunarVfsInfo          *info;
 
-  gint           watch_id;
+  ThunarVfsMonitorHandle *handle;
 };
 
 
@@ -148,6 +151,7 @@ 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->reload = thunar_local_file_reload;
 }
 
 
@@ -166,7 +170,7 @@ thunar_local_file_finalize (GObject *object)
   ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (object);
 
 #ifndef G_DISABLE_CHECKS
-  if (G_UNLIKELY (local_file->watch_id != 0))
+  if (G_UNLIKELY (local_file->handle != NULL))
     {
       g_error ("Attempt to finalize a ThunarLocalFile, which "
                "is still being watched for changes");
@@ -413,12 +417,12 @@ thunar_local_file_watch (ThunarFile *file)
 {
   ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
 
-  g_return_if_fail (local_file->watch_id == 0);
+  g_return_if_fail (local_file->handle == NULL);
 
   /* take a reference on the VFS monitor for this instance */
   if (G_UNLIKELY (monitor == NULL))
     {
-      monitor = thunar_vfs_monitor_get_default ();
+      monitor = thunar_vfs_monitor_get ();
       g_object_add_weak_pointer (G_OBJECT (monitor), (gpointer) &monitor);
     }
   else
@@ -426,14 +430,8 @@ 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,
-                                                      thunar_local_file_monitor,
-                                                      local_file);
-#else
-  (void) &thunar_local_file_monitor;
-#endif
+  /* add us to the file monitor */
+  local_file->handle = thunar_vfs_monitor_add_file (monitor, local_file->info->uri, thunar_local_file_monitor, local_file);
 }
 
 
@@ -441,15 +439,13 @@ 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);
+  g_return_if_fail (local_file->handle != NULL);
 
   /* remove our VFS info from the monitor */
-  thunar_vfs_monitor_remove (monitor, local_file->watch_id);
-  local_file->watch_id = 0;
-#endif
+  thunar_vfs_monitor_remove (monitor, local_file->handle);
+  local_file->handle = NULL;
 
   /* release our reference on the VFS monitor */
   g_object_unref (G_OBJECT (monitor));
@@ -458,23 +454,63 @@ thunar_local_file_unwatch (ThunarFile *file)
 
 
 static void
-thunar_local_file_monitor (ThunarVfsMonitor     *monitor,
-                           ThunarVfsMonitorEvent event,
-                           ThunarVfsInfo        *info,
-                           gpointer              user_data)
+thunar_local_file_reload (ThunarFile *file)
+{
+  ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
+  ThunarVfsInfo   *info;
+
+  /* re-query the file info */
+  info = thunar_vfs_info_new_for_uri (local_file->info->uri, NULL);
+  if (G_UNLIKELY (info == NULL))
+    {
+      /* the file is no longer present */
+      gtk_object_destroy (GTK_OBJECT (file));
+    }
+  else
+    {
+      /* apply the new info... */
+      thunar_vfs_info_unref (local_file->info);
+      local_file->info = info;
+
+      /* ... and tell others */
+      thunar_file_changed (file);
+    }
+}
+
+
+
+static void
+thunar_local_file_monitor (ThunarVfsMonitor       *monitor,
+                           ThunarVfsMonitorHandle *handle,
+                           ThunarVfsMonitorEvent   event,
+                           ThunarVfsURI           *handle_uri,
+                           ThunarVfsURI           *event_uri,
+                           gpointer                user_data)
 {
+  ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (user_data);
+
+  g_return_if_fail (THUNAR_VFS_IS_MONITOR (monitor));
+  g_return_if_fail (THUNAR_VFS_IS_URI (handle_uri));
+  g_return_if_fail (THUNAR_VFS_IS_URI (event_uri));
+  g_return_if_fail (THUNAR_IS_LOCAL_FILE (local_file));
+  g_return_if_fail (local_file->handle == handle);
+  g_return_if_fail (thunar_vfs_uri_equal (local_file->info->uri, handle_uri));
+
+  /* just to be sure... */
+  if (G_UNLIKELY (!thunar_vfs_uri_equal (handle_uri, event_uri)))
+    return;
+
   switch (event)
     {
-    case THUNAR_VFS_MONITOR_CHANGED:
+    case THUNAR_VFS_MONITOR_EVENT_CHANGED:
+    case THUNAR_VFS_MONITOR_EVENT_CREATED:
+      // FIXME: We need to reload the file data here
       thunar_file_changed (THUNAR_FILE (user_data));
       break;
 
-    case THUNAR_VFS_MONITOR_DELETED:
+    case THUNAR_VFS_MONITOR_EVENT_DELETED:
       gtk_object_destroy (GTK_OBJECT (user_data));
       break;
-
-    default:
-      break;
     }
 }
 
diff --git a/thunar/thunar-local-folder.c b/thunar/thunar-local-folder.c
index fe4c81a50..269858eb5 100644
--- a/thunar/thunar-local-folder.c
+++ b/thunar/thunar-local-folder.c
@@ -60,8 +60,6 @@ static void        thunar_local_folder_infos_ready                (ThunarVfsJob
                                                                    ThunarLocalFolder      *local_folder);
 static void        thunar_local_folder_finished                   (ThunarVfsJob           *job,
                                                                    ThunarLocalFolder      *local_folder);
-static void        thunar_local_folder_corresponding_file_changed (ThunarFile             *file,
-                                                                   ThunarLocalFolder      *local_folder);
 static void        thunar_local_folder_corresponding_file_destroy (ThunarFile             *file,
                                                                    ThunarLocalFolder      *local_folder);
 static void        thunar_local_folder_file_destroy               (ThunarFile             *file,
@@ -78,13 +76,16 @@ struct _ThunarLocalFolder
 {
   GtkObject __parent__;
 
-  ThunarVfsJob *job;
+  ThunarVfsJob           *job;
+
+  ThunarFile             *corresponding_file;
+  GSList                 *files;
 
-  ThunarFile *corresponding_file;
-  GSList     *files;
+  GClosure               *file_destroy_closure;
+  gint                    file_destroy_id;
 
-  GClosure   *file_destroy_closure;
-  gint        file_destroy_id;
+  ThunarVfsMonitor       *monitor;
+  ThunarVfsMonitorHandle *handle;
 };
 
 
@@ -124,15 +125,18 @@ thunar_local_folder_folder_init (ThunarFolderIface *iface)
 
 
 static void
-thunar_local_folder_init (ThunarLocalFolder *folder)
+thunar_local_folder_init (ThunarLocalFolder *local_folder)
 {
   /* lookup the id for the "destroy" signal of ThunarFile's */
-  folder->file_destroy_id = g_signal_lookup ("destroy", THUNAR_TYPE_FILE);
+  local_folder->file_destroy_id = g_signal_lookup ("destroy", THUNAR_TYPE_FILE);
 
   /* generate the closure to connect to the "destroy" signal of all files */
-  folder->file_destroy_closure = g_cclosure_new (G_CALLBACK (thunar_local_folder_file_destroy), folder, NULL);
-  g_closure_ref (folder->file_destroy_closure);
-  g_closure_sink (folder->file_destroy_closure);
+  local_folder->file_destroy_closure = g_cclosure_new (G_CALLBACK (thunar_local_folder_file_destroy), local_folder, NULL);
+  g_closure_ref (local_folder->file_destroy_closure);
+  g_closure_sink (local_folder->file_destroy_closure);
+
+  /* connect to the file alteration monitor */
+  local_folder->monitor = thunar_vfs_monitor_get ();
 }
 
 
@@ -145,6 +149,11 @@ thunar_local_folder_finalize (GObject *object)
 
   g_return_if_fail (THUNAR_IS_LOCAL_FOLDER (local_folder));
 
+  /* disconnect from the file alteration monitor */
+  if (G_LIKELY (local_folder->handle != NULL))
+    thunar_vfs_monitor_remove (local_folder->monitor, local_folder->handle);
+  g_object_unref (G_OBJECT (local_folder->monitor));
+
   /* cancel the pending job (if any) */
   if (G_UNLIKELY (local_folder->job != NULL))
     {
@@ -156,9 +165,6 @@ thunar_local_folder_finalize (GObject *object)
   /* disconnect from the corresponding file */
   if (G_LIKELY (local_folder->corresponding_file != NULL))
     {
-      /* unwatch the corresponding file */
-      thunar_file_unwatch (local_folder->corresponding_file);
-
       /* drop the reference */
       g_signal_handlers_disconnect_matched (G_OBJECT (local_folder->corresponding_file),
                                             G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, local_folder);
@@ -253,6 +259,7 @@ thunar_local_folder_infos_ready (ThunarVfsJob      *job,
   ThunarFile *file;
   GSList     *nfiles = NULL;
   GSList     *lp;
+  GSList     *p;
 
   g_return_if_fail (THUNAR_IS_LOCAL_FOLDER (local_folder));
   g_return_if_fail (local_folder->job == job);
@@ -261,7 +268,20 @@ thunar_local_folder_infos_ready (ThunarVfsJob      *job,
   /* add the new files */
   for (lp = infos; lp != NULL; lp = lp->next)
     {
+      /* get the file corresponding to the info */
       file = thunar_local_file_get_for_info (lp->data);
+
+      /* verify that we don't already have that file */
+      for (p = local_folder->files; p != NULL; p = p->next)
+        if (G_UNLIKELY (p->data == file))
+          break;
+      if (G_UNLIKELY (p != NULL))
+        {
+          g_object_unref (G_OBJECT (file));
+          continue;
+        }
+
+      /* add the file */
       nfiles = g_slist_prepend (nfiles, file);
       local_folder->files = g_slist_prepend (local_folder->files, file);
 
@@ -297,23 +317,6 @@ thunar_local_folder_finished (ThunarVfsJob      *job,
 
 
 
-static void
-thunar_local_folder_corresponding_file_changed (ThunarFile        *file,
-                                                ThunarLocalFolder *local_folder)
-{
-  g_return_if_fail (THUNAR_IS_FILE (file));
-  g_return_if_fail (THUNAR_IS_LOCAL_FOLDER (local_folder));
-  g_return_if_fail (local_folder->corresponding_file == file);
-
-  /* rescan the directory (FIXME) */
-#if 0
-  if (!thunar_local_folder_rescan (local_folder, NULL))
-    gtk_object_destroy (GTK_OBJECT (local_folder));
-#endif
-}
-
-
-
 static void
 thunar_local_folder_corresponding_file_destroy (ThunarFile        *file,
                                                 ThunarLocalFolder *local_folder)
@@ -345,6 +348,81 @@ thunar_local_folder_file_destroy (ThunarFile        *file,
 
 
 
+static void
+thunar_local_folder_monitor (ThunarVfsMonitor       *monitor,
+                             ThunarVfsMonitorHandle *handle,
+                             ThunarVfsMonitorEvent   event,
+                             ThunarVfsURI           *handle_uri,
+                             ThunarVfsURI           *event_uri,
+                             gpointer                user_data)
+{
+  ThunarLocalFolder *local_folder = THUNAR_LOCAL_FOLDER (user_data);
+  ThunarFile        *file;
+  GSList            *lp;
+
+  g_return_if_fail (THUNAR_VFS_IS_MONITOR (monitor));
+  g_return_if_fail (THUNAR_VFS_IS_URI (handle_uri));
+  g_return_if_fail (THUNAR_VFS_IS_URI (event_uri));
+  g_return_if_fail (THUNAR_IS_LOCAL_FOLDER (local_folder));
+  g_return_if_fail (local_folder->monitor == monitor);
+  g_return_if_fail (local_folder->handle == handle);
+
+  /* determine the file for the uri */
+  file = thunar_file_get_for_uri (event_uri, NULL);
+  if (G_UNLIKELY (file == NULL))
+    return;
+
+  /* check the event */
+  switch (event)
+    {
+    case THUNAR_VFS_MONITOR_EVENT_CHANGED:
+    case THUNAR_VFS_MONITOR_EVENT_CREATED:
+      if (G_LIKELY (file != local_folder->corresponding_file))
+        {
+          /* check if we already ship the file */
+          for (lp = local_folder->files; lp != NULL; lp = lp->next)
+            if (G_UNLIKELY (lp->data == file))
+              break;
+
+          /* if we don't have it, add it */
+          if (G_UNLIKELY (lp == NULL))
+            {
+              /* prepend it to our internal list */
+              g_signal_connect_closure_by_id (G_OBJECT (file), local_folder->file_destroy_id,
+                                              0, local_folder->file_destroy_closure, TRUE);
+              local_folder->files = g_slist_prepend (local_folder->files, file);
+              g_object_ref (G_OBJECT (file));
+
+              /* tell others about the new file */
+              lp = g_slist_prepend (NULL, file);
+              thunar_folder_files_added (THUNAR_FOLDER (local_folder), lp);
+              g_slist_free (lp);
+            }
+          else
+            {
+              /* force an update on the file */
+              thunar_file_reload (file);
+            }
+        }
+      else
+        {
+          /* force an update on the corresponding file */
+          thunar_file_reload (file);
+        }
+      break;
+
+    case THUNAR_VFS_MONITOR_EVENT_DELETED:
+      /* drop the file */
+      gtk_object_destroy (GTK_OBJECT (file));
+      break;
+    }
+
+  /* cleanup */
+  g_object_unref (G_OBJECT (file));
+}
+
+
+
 /**
  * thunar_local_folder_get_for_file:
  * @file  : a #ThunarFile instance.
@@ -378,15 +456,11 @@ thunar_local_folder_get_for_file (ThunarLocalFile *local_file,
       local_folder->corresponding_file = THUNAR_FILE (local_file);
       g_object_ref (G_OBJECT (local_file));
 
-      /* watch the corresponding file for changes */
-      thunar_file_watch (local_folder->corresponding_file);
-
       /* drop the floating reference */
       g_assert (GTK_OBJECT_FLOATING (local_folder));
       g_object_ref (G_OBJECT (local_folder));
       gtk_object_sink (GTK_OBJECT (local_folder));
 
-      g_signal_connect (G_OBJECT (local_file), "changed", G_CALLBACK (thunar_local_folder_corresponding_file_changed), local_folder);
       g_signal_connect (G_OBJECT (local_file), "destroy", G_CALLBACK (thunar_local_folder_corresponding_file_destroy), local_folder);
 
       g_object_set_data (G_OBJECT (local_file), "thunar-local-folder", local_folder);
@@ -396,6 +470,10 @@ thunar_local_folder_get_for_file (ThunarLocalFile *local_file,
       g_signal_connect (local_folder->job, "error", G_CALLBACK (thunar_local_folder_error), local_folder);
       g_signal_connect (local_folder->job, "finished", G_CALLBACK (thunar_local_folder_finished), local_folder);
       g_signal_connect (local_folder->job, "infos-ready", G_CALLBACK (thunar_local_folder_infos_ready), local_folder);
+
+      /* add us to the file alteration monitor */
+      local_folder->handle = thunar_vfs_monitor_add_directory (local_folder->monitor, thunar_file_get_uri (THUNAR_FILE (local_file)),
+                                                               thunar_local_folder_monitor, local_folder);
     }
 
   return THUNAR_FOLDER (local_folder);
-- 
GitLab