From 946027e708955e56ed083e7671fb3982647afafc Mon Sep 17 00:00:00 2001
From: Benedikt Meurer <benny@xfce.org>
Date: Sun, 12 Feb 2006 16:33:34 +0000
Subject: [PATCH] 2006-02-12	Benedikt Meurer <benny@xfce.org>

	* thunar/thunar-file-monitor.{c,h}, thunar/Makefile.am: Add new class
	  ThunarFileMonitor, which allows other objects stay informed about
	  changes to ThunarFile's without having to connect signal handlers to
	  every ThunarFile. Bug #1447.
	* thunar/thunar-file.c: Emit ThunarFileMonitor signals as appropriate.
	  Bug #1447.
	* thunar/thunar-folder.c, thunar/thunar-list-model.c: Use the newly
	  added ThunarFileMonitor to monitor files for changes and deletion
	  without having to connect and disconnect signal handlers to each and
	  every file. Bug #1447.




(Old svn revision: 19848)
---
 ChangeLog                    |  13 +++
 thunar/Makefile.am           |   2 +
 thunar/thunar-file-monitor.c | 204 +++++++++++++++++++++++++++++++++++
 thunar/thunar-file-monitor.h |  46 ++++++++
 thunar/thunar-file.c         |  20 +++-
 thunar/thunar-folder.c       | 111 +++++++------------
 thunar/thunar-list-model.c   |  52 ++++-----
 7 files changed, 344 insertions(+), 104 deletions(-)
 create mode 100644 thunar/thunar-file-monitor.c
 create mode 100644 thunar/thunar-file-monitor.h

diff --git a/ChangeLog b/ChangeLog
index b15d75352..040a0d9b1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-02-12	Benedikt Meurer <benny@xfce.org>
+
+	* thunar/thunar-file-monitor.{c,h}, thunar/Makefile.am: Add new class
+	  ThunarFileMonitor, which allows other objects stay informed about
+	  changes to ThunarFile's without having to connect signal handlers to
+	  every ThunarFile. Bug #1447.
+	* thunar/thunar-file.c: Emit ThunarFileMonitor signals as appropriate.
+	  Bug #1447.
+	* thunar/thunar-folder.c, thunar/thunar-list-model.c: Use the newly
+	  added ThunarFileMonitor to monitor files for changes and deletion
+	  without having to connect and disconnect signal handlers to each and
+	  every file. Bug #1447.
+
 2006-02-12	Benedikt Meurer <benny@xfce.org>
 
 	* thunar/thunar-throbber-fallback.png: Import better throbber fallback
diff --git a/thunar/Makefile.am b/thunar/Makefile.am
index 115eb4725..c01d61dba 100644
--- a/thunar/Makefile.am
+++ b/thunar/Makefile.am
@@ -51,6 +51,8 @@ Thunar_SOURCES =							\
 	thunar-enum-types.h						\
 	thunar-file.c							\
 	thunar-file.h							\
+	thunar-file-monitor.c						\
+	thunar-file-monitor.h						\
 	thunar-folder.c							\
 	thunar-folder.h							\
 	thunar-gdk-extensions.c						\
diff --git a/thunar/thunar-file-monitor.c b/thunar/thunar-file-monitor.c
new file mode 100644
index 000000000..8733bc613
--- /dev/null
+++ b/thunar/thunar-file-monitor.c
@@ -0,0 +1,204 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2006 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-file-monitor.h>
+
+
+
+/* Signal identifiers */
+enum
+{
+  FILE_CHANGED,
+  FILE_DESTROYED,
+  LAST_SIGNAL,
+};
+
+
+
+static void thunar_file_monitor_class_init (ThunarFileMonitorClass *klass);
+
+
+
+struct _ThunarFileMonitorClass
+{
+  GObjectClass __parent__;
+};
+
+struct _ThunarFileMonitor
+{
+  GObject __parent__;
+};
+
+
+
+static ThunarFileMonitor *file_monitor_default;
+static guint              file_monitor_signals[LAST_SIGNAL];
+
+
+
+GType
+thunar_file_monitor_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+
+  if (G_UNLIKELY (type == G_TYPE_INVALID))
+    {
+      static const GTypeInfo info =
+      {
+        sizeof (ThunarFileMonitorClass),
+        NULL,
+        NULL,
+        (GClassInitFunc) thunar_file_monitor_class_init,
+        NULL,
+        NULL,
+        sizeof (ThunarFileMonitor),
+        0,
+        NULL,
+        NULL,
+      };
+
+      type = g_type_register_static (G_TYPE_OBJECT, I_("ThunarFileMonitor"), &info, 0);
+    }
+
+  return type;
+}
+
+
+
+static void
+thunar_file_monitor_class_init (ThunarFileMonitorClass *klass)
+{
+  /**
+   * ThunarFileMonitor::file-changed:
+   * @file_monitor : the default #ThunarFileMonitor.
+   * @file         : the #ThunarFile that changed.
+   *
+   * This signal is emitted on @file_monitor whenever any of the currently
+   * existing #ThunarFile instances changes. @file identifies the instance
+   * that changed.
+   **/
+  file_monitor_signals[FILE_CHANGED] =
+    g_signal_new (I_("file-changed"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_NO_HOOKS,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, THUNAR_TYPE_FILE);
+
+  /**
+   * ThunarFileMonitor::file-destroyed:
+   * @file_monitor : the default #ThunarFileMonitor.
+   * @file         : the #ThunarFile that is about to be destroyed.
+   *
+   * This signal is emitted on @file_monitor whenever any of the currently
+   * existing #ThunarFile instances is about to be destroyed. @file identifies
+   * the instance that is about to be destroyed.
+   *
+   * Note that this signal is only emitted if @file is explicitly destroyed,
+   * i.e. because Thunar noticed that it was removed from disk, it is not
+   * emitted when the last reference on @file is released.
+   **/
+  file_monitor_signals[FILE_DESTROYED] =
+    g_signal_new (I_("file-destroyed"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_NO_HOOKS,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, THUNAR_TYPE_FILE);
+}
+
+
+
+/**
+ * thunar_file_monitor_get_default:
+ *
+ * Returns a reference to the default #ThunarFileMonitor
+ * instance. The #ThunarFileMonitor default instance can
+ * be used to monitor the lifecycle of all currently existing
+ * #ThunarFile instances. The ::file-changed and ::file-destroyed
+ * signals will be emitted whenever any of the currently
+ * existing #ThunarFile<!---->s is changed or destroyed.
+ *
+ * The caller is responsible to free the returned instance
+ * using g_object_unref() when no longer needed.
+ *
+ * Return value: the default #ThunarFileMonitor instance.
+ **/
+ThunarFileMonitor*
+thunar_file_monitor_get_default (void)
+{
+  if (G_UNLIKELY (file_monitor_default == NULL))
+    {
+      /* allocate the default monitor */
+      file_monitor_default = g_object_new (THUNAR_TYPE_FILE_MONITOR, NULL);
+      g_object_add_weak_pointer (G_OBJECT (file_monitor_default), 
+                                 (gpointer) &file_monitor_default);
+    }
+  else
+    {
+      /* take a reference for the caller */
+      g_object_ref (G_OBJECT (file_monitor_default));
+    }
+
+  return file_monitor_default;
+}
+
+
+
+/**
+ * thunar_file_monitor_file_changed:
+ * @file : a #ThunarFile.
+ *
+ * Emits the ::file-changed signal on the default
+ * #ThunarFileMonitor (if any). This method should
+ * only be used by #ThunarFile.
+ **/
+void
+thunar_file_monitor_file_changed (ThunarFile *file)
+{
+  g_return_if_fail (THUNAR_IS_FILE (file));
+
+  if (G_LIKELY (file_monitor_default != NULL))
+    g_signal_emit (G_OBJECT (file_monitor_default), file_monitor_signals[FILE_CHANGED], 0, file);
+}
+
+
+
+/**
+ * thunar_file_monitor_file_destroyed.
+ * @file : a #ThunarFile.
+ *
+ * Emits the ::file-destroyed signal on the default
+ * #ThunarFileMonitor (if any). This method should
+ * only be used by #ThunarFile.
+ **/
+void
+thunar_file_monitor_file_destroyed (ThunarFile *file)
+{
+  g_return_if_fail (THUNAR_IS_FILE (file));
+
+  if (G_LIKELY (file_monitor_default != NULL))
+    g_signal_emit (G_OBJECT (file_monitor_default), file_monitor_signals[FILE_DESTROYED], 0, file);
+}
+
+
diff --git a/thunar/thunar-file-monitor.h b/thunar/thunar-file-monitor.h
new file mode 100644
index 000000000..3ee4dd7ca
--- /dev/null
+++ b/thunar/thunar-file-monitor.h
@@ -0,0 +1,46 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2006 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_FILE_MONITOR_H__
+#define __THUNAR_FILE_MONITOR_H__
+
+#include <thunar/thunar-file.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarFileMonitorClass ThunarFileMonitorClass;
+typedef struct _ThunarFileMonitor      ThunarFileMonitor;
+
+#define THUNAR_TYPE_FILE_MONITOR            (thunar_file_monitor_get_type ())
+#define THUNAR_FILE_MONITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_FILE_MONITOR, ThunarFileMonitor))
+#define THUNAR_FILE_MONITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_FILE_MONITOR, ThunarFileMonitorClass))
+#define THUNAR_IS_FILE_MONITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_FILE_MONITOR))
+#define THUNAR_IS_FILE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_FILE_MONITOR))
+#define THUNAR_FILE_MONITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_FILE_MONITOR, ThunarFileMonitorClass))
+
+GType              thunar_file_monitor_get_type       (void) G_GNUC_CONST;
+
+ThunarFileMonitor *thunar_file_monitor_get_default    (void);
+
+void               thunar_file_monitor_file_changed   (ThunarFile *file);
+void               thunar_file_monitor_file_destroyed (ThunarFile *file);
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_FILE_MONITOR_H__ */
diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c
index dcc9d1afa..acd9d0789 100644
--- a/thunar/thunar-file.c
+++ b/thunar/thunar-file.c
@@ -44,6 +44,7 @@
 #include <thunar/thunar-application.h>
 #include <thunar/thunar-chooser-dialog.h>
 #include <thunar/thunar-file.h>
+#include <thunar/thunar-file-monitor.h>
 #include <thunar/thunar-gobject-extensions.h>
 
 
@@ -457,6 +458,9 @@ thunar_file_info_changed (ThunarxFileInfo *file_info)
 
   /* notify about changes of the display-name property */
   g_object_notify (G_OBJECT (file_info), "display-name");
+
+  /* tell the file monitor that this file changed */
+  thunar_file_monitor_file_changed (THUNAR_FILE (file_info));
 }
 
 
@@ -1787,7 +1791,21 @@ thunar_file_destroy (ThunarFile *file)
   g_return_if_fail (THUNAR_IS_FILE (file));
 
   if (G_LIKELY ((file->flags & THUNAR_FILE_IN_DESTRUCTION) == 0))
-    g_object_run_dispose (G_OBJECT (file));
+    {
+      /* take an additional reference on the file, as the file-destroyed
+       * invocation may already release the last reference.
+       */
+      g_object_ref (G_OBJECT (file));
+
+      /* tell the file monitor that this file was destroyed */
+      thunar_file_monitor_file_destroyed (file);
+
+      /* run the dispose handler */
+      g_object_run_dispose (G_OBJECT (file));
+
+      /* release our reference */
+      g_object_unref (G_OBJECT (file));
+    }
 }
 
 
diff --git a/thunar/thunar-folder.c b/thunar/thunar-folder.c
index 512b8b4a3..39508e2aa 100644
--- a/thunar/thunar-folder.c
+++ b/thunar/thunar-folder.c
@@ -21,6 +21,7 @@
 #include <config.h>
 #endif
 
+#include <thunar/thunar-file-monitor.h>
 #include <thunar/thunar-folder.h>
 #include <thunar/thunar-gobject-extensions.h>
 
@@ -63,7 +64,8 @@ static void     thunar_folder_corresponding_file_destroy  (ThunarFile
                                                            ThunarFolder           *folder);
 static void     thunar_folder_corresponding_file_renamed  (ThunarFile             *file,
                                                            ThunarFolder           *folder);
-static void     thunar_folder_file_destroy                (ThunarFile             *file,
+static void     thunar_folder_file_destroyed              (ThunarFileMonitor      *file_monitor,
+                                                           ThunarFile             *file,
                                                            ThunarFolder           *folder);
 static void     thunar_folder_monitor                     (ThunarVfsMonitor       *monitor,
                                                            ThunarVfsMonitorHandle *handle,
@@ -97,8 +99,7 @@ struct _ThunarFolder
   GList                  *previous_files;
   GList                  *files;
 
-  GClosure               *file_destroy_closure;
-  gint                    file_destroy_id;
+  ThunarFileMonitor      *file_monitor;
 
   ThunarVfsMonitor       *monitor;
   ThunarVfsMonitorHandle *handle;
@@ -220,13 +221,9 @@ thunar_folder_class_init (ThunarFolderClass *klass)
 static void
 thunar_folder_init (ThunarFolder *folder)
 {
-  /* lookup the id for the "destroy" signal of ThunarFile's */
-  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_folder_file_destroy), folder, NULL);
-  g_closure_ref (folder->file_destroy_closure);
-  g_closure_sink (folder->file_destroy_closure);
+  /* connect to the ThunarFileMonitor instance */
+  folder->file_monitor = thunar_file_monitor_get_default ();
+  g_signal_connect (G_OBJECT (folder->file_monitor), "file-destroyed", G_CALLBACK (thunar_folder_file_destroyed), folder);
 
   /* connect to the file alteration monitor */
   folder->monitor = thunar_vfs_monitor_get_default ();
@@ -238,7 +235,10 @@ static void
 thunar_folder_finalize (GObject *object)
 {
   ThunarFolder *folder = THUNAR_FOLDER (object);
-  GList        *lp;
+
+  /* disconnect from the ThunarFileMonitor instance */
+  g_signal_handlers_disconnect_by_func (G_OBJECT (folder->file_monitor), thunar_folder_file_destroyed, folder);
+  g_object_unref (G_OBJECT (folder->file_monitor));
 
   /* disconnect from the file alteration monitor */
   if (G_LIKELY (folder->handle != NULL))
@@ -262,25 +262,11 @@ thunar_folder_finalize (GObject *object)
       g_object_unref (G_OBJECT (folder->corresponding_file));
     }
 
-  /* drop all previous files (if any) */
-  if (G_UNLIKELY (folder->previous_files != NULL))
-    {
-      /* actually drop the list of previous files */
-      g_list_foreach (folder->previous_files, (GFunc) g_object_unref, NULL);
-      g_list_free (folder->previous_files);
-    }
+  /* release references to the previous files */
+  thunar_file_list_free (folder->previous_files);
 
   /* release references to the current files */
-  for (lp = folder->files; lp != NULL; lp = lp->next)
-    {
-      g_signal_handlers_disconnect_matched (G_OBJECT (lp->data), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE,
-                                            folder->file_destroy_id, 0, folder->file_destroy_closure, NULL, NULL);
-      g_object_unref (G_OBJECT (lp->data));
-    }
-  g_list_free (folder->files);
-
-  /* drop the "destroy" closure */
-  g_closure_unref (folder->file_destroy_closure);
+  thunar_file_list_free (folder->files);
 
   (*G_OBJECT_CLASS (thunar_folder_parent_class)->finalize) (object);
 }
@@ -365,10 +351,6 @@ thunar_folder_infos_ready (ThunarVfsJob *job,
               /* yep, that's a new file then */
               nfiles = g_list_prepend (nfiles, file);
             }
-
-          /* connect the "destroy" signal */
-          g_signal_connect_closure_by_id (G_OBJECT (file), folder->file_destroy_id,
-                                          0, folder->file_destroy_closure, TRUE);
         }
     }
   else
@@ -387,10 +369,6 @@ thunar_folder_infos_ready (ThunarVfsJob *job,
 
           /* ...and replace it with the file */
           lp->data = file;
-
-          /* connect the "destroy" signal */
-          g_signal_connect_closure_by_id (G_OBJECT (file), folder->file_destroy_id,
-                                          0, folder->file_destroy_closure, TRUE);
         }
     }
 
@@ -427,8 +405,7 @@ thunar_folder_finished (ThunarVfsJob *job,
       g_signal_emit (G_OBJECT (folder), folder_signals[FILES_REMOVED], 0, folder->previous_files);
 
       /* actually drop the list of previous files */
-      g_list_foreach (folder->previous_files, (GFunc) g_object_unref, NULL);
-      g_list_free (folder->previous_files);
+      thunar_file_list_free (folder->previous_files);
       folder->previous_files = NULL;
     }
 
@@ -469,37 +446,38 @@ thunar_folder_corresponding_file_renamed (ThunarFile   *file,
   g_return_if_fail (THUNAR_IS_FOLDER (folder));
   g_return_if_fail (folder->corresponding_file == file);
 
-  /* re-register the VFS monitor handle if already connected */
-  if (G_LIKELY (folder->handle != NULL))
-    {
-      thunar_vfs_monitor_remove (folder->monitor, folder->handle);
-      folder->handle = thunar_vfs_monitor_add_directory (folder->monitor, thunar_file_get_path (file),
-                                                         thunar_folder_monitor, folder);
-    }
+  /* reload the folder contents as all paths need to be updated */
+  thunar_folder_reload (folder);
 }
 
 
 
 static void
-thunar_folder_file_destroy (ThunarFile   *file,
-                            ThunarFolder *folder)
+thunar_folder_file_destroyed (ThunarFileMonitor *file_monitor,
+                              ThunarFile        *file,
+                              ThunarFolder      *folder)
 {
-  GList files;
+  GList  files;
+  GList *lp;
 
   g_return_if_fail (THUNAR_IS_FILE (file));
   g_return_if_fail (THUNAR_IS_FOLDER (folder));
-  g_return_if_fail (g_list_find (folder->files, file) != NULL);
+  g_return_if_fail (THUNAR_IS_FILE_MONITOR (file_monitor));
 
-  /* disconnect from the file */
-  g_signal_handlers_disconnect_matched (G_OBJECT (file), G_SIGNAL_MATCH_CLOSURE, 0, 0, folder->file_destroy_closure, NULL, NULL);
-  folder->files = g_list_remove (folder->files, file);
+  /* check if we have that file */
+  lp = g_list_find (folder->files, file);
+  if (G_LIKELY (lp != NULL))
+    {
+      /* remove the file from our list */
+      folder->files = g_list_delete_link (folder->files, lp);
 
-  /* tell everybody that the file is gone */
-  files.data = file; files.next = files.prev = NULL;
-  g_signal_emit (G_OBJECT (folder), folder_signals[FILES_REMOVED], 0, &files);
+      /* tell everybody that the file is gone */
+      files.data = file; files.next = files.prev = NULL;
+      g_signal_emit (G_OBJECT (folder), folder_signals[FILES_REMOVED], 0, &files);
 
-  /* drop our reference to the file */
-  g_object_unref (G_OBJECT (file));
+      /* drop our reference to the file */
+      g_object_unref (G_OBJECT (file));
+    }
 }
 
 
@@ -540,7 +518,6 @@ thunar_folder_monitor (ThunarVfsMonitor       *monitor,
             return;
 
           /* prepend it to our internal list */
-          g_signal_connect_closure_by_id (G_OBJECT (file), folder->file_destroy_id, 0, folder->file_destroy_closure, TRUE);
           folder->files = g_list_prepend (folder->files, file);
 
           /* tell others about the new file */
@@ -691,8 +668,6 @@ thunar_folder_get_loading (const ThunarFolder *folder)
 void
 thunar_folder_reload (ThunarFolder *folder)
 {
-  GList *lp;
-
   g_return_if_fail (THUNAR_IS_FOLDER (folder));
 
   /* check if we are currently connect to a job */
@@ -710,20 +685,8 @@ thunar_folder_reload (ThunarFolder *folder)
       folder->handle = NULL;
     }
 
-  /* drop all previous files (if any) */
-  if (G_UNLIKELY (folder->previous_files != NULL))
-    {
-      /* actually drop the list of previous files */
-      g_list_foreach (folder->previous_files, (GFunc) g_object_unref, NULL);
-      g_list_free (folder->previous_files);
-    }
-
-  /* disconnect signals from the current files */
-  for (lp = folder->files; lp != NULL; lp = lp->next)
-    {
-      g_signal_handlers_disconnect_matched (G_OBJECT (lp->data), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE,
-                                            folder->file_destroy_id, 0, folder->file_destroy_closure, NULL, NULL);
-    }
+  /* drop all previous files */
+  thunar_file_list_free (folder->previous_files);
 
   /* remember the current files as previous files */
   folder->previous_files = folder->files;
diff --git a/thunar/thunar-list-model.c b/thunar/thunar-list-model.c
index fe11ea44f..c00ec7ac4 100644
--- a/thunar/thunar-list-model.c
+++ b/thunar/thunar-list-model.c
@@ -28,6 +28,7 @@
 #include <string.h>
 #endif
 
+#include <thunar/thunar-file-monitor.h>
 #include <thunar/thunar-gobject-extensions.h>
 #include <thunar/thunar-list-model.h>
 
@@ -136,7 +137,8 @@ static gboolean           thunar_list_model_remove                (ThunarListMod
                                                                    GtkTreeIter            *iter,
                                                                    gboolean                silently);
 static void               thunar_list_model_sort                  (ThunarListModel        *store);
-static void               thunar_list_model_file_changed          (ThunarFile             *file,
+static void               thunar_list_model_file_changed          (ThunarFileMonitor      *file_monitor,
+                                                                   ThunarFile             *file,
                                                                    ThunarListModel        *store);
 static void               thunar_list_model_folder_destroy        (ThunarFolder           *folder,
                                                                    ThunarListModel        *store);
@@ -189,13 +191,13 @@ struct _ThunarListModel
   ThunarFolder  *folder;
   gboolean       show_hidden;
 
-  ThunarVfsVolumeManager *volume_manager;
-
-  /* ids and closures for the "changed" signal of
-   * ThunarFile's used to speed up signal registrations.
+  /* Use the shared ThunarFileMonitor instance, so we
+   * do not need to connect "changed" handler to every
+   * file in the model.
    */
-  GClosure      *file_changed_closure;
-  gint           file_changed_id;
+  ThunarFileMonitor      *file_monitor;
+
+  ThunarVfsVolumeManager *volume_manager;
 
   /* ids for the "row-inserted" and "row-deleted" signals
    * of GtkTreeModel to speed up folder changing.
@@ -415,9 +417,6 @@ thunar_list_model_init (ThunarListModel *store)
 
   store->volume_manager       = thunar_vfs_volume_manager_get_default ();
 
-  store->file_changed_closure = g_cclosure_new_object (G_CALLBACK (thunar_list_model_file_changed), G_OBJECT (store));
-  store->file_changed_id      = g_signal_lookup ("changed", THUNAR_TYPE_FILE);
-
   store->row_inserted_id      = g_signal_lookup ("row-inserted", GTK_TYPE_TREE_MODEL);
   store->row_deleted_id       = g_signal_lookup ("row-deleted", GTK_TYPE_TREE_MODEL);
 
@@ -425,9 +424,11 @@ thunar_list_model_init (ThunarListModel *store)
   store->sort_sign            = 1;
   store->sort_func            = sort_by_name;
 
-  /* take over ownership of the "changed" closure */
-  g_closure_ref (store->file_changed_closure);
-  g_closure_sink (store->file_changed_closure);
+  /* connect to the shared ThunarFileMonitor, so we don't need to
+   * connect "changed" to every single ThunarFile we own.
+   */
+  store->file_monitor = thunar_file_monitor_get_default ();
+  g_signal_connect (G_OBJECT (store->file_monitor), "file-changed", G_CALLBACK (thunar_list_model_file_changed), store);
 }
 
 
@@ -456,8 +457,9 @@ thunar_list_model_finalize (GObject *object)
   /* disconnect from the volume manager */
   g_object_unref (G_OBJECT (store->volume_manager));
 
-  /* get rid of the "changed" closure */
-  g_closure_unref (store->file_changed_closure);
+  /* disconnect from the file monitor */
+  g_signal_handlers_disconnect_by_func (G_OBJECT (store->file_monitor), thunar_list_model_file_changed, store);
+  g_object_unref (G_OBJECT (store->file_monitor));
 
   (*G_OBJECT_CLASS (thunar_list_model_parent_class)->finalize) (object);
 }
@@ -1034,8 +1036,6 @@ thunar_list_model_remove (ThunarListModel *store,
   path = thunar_list_model_get_path (GTK_TREE_MODEL (store), iter);
 
   /* delete data associated with this row */
-  g_signal_handlers_disconnect_matched (G_OBJECT (row->file), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE,
-                                        store->file_changed_id, 0, store->file_changed_closure, NULL, NULL);
   g_object_unref (row->file);
 
   /* remove the link from the list */
@@ -1131,8 +1131,9 @@ thunar_list_model_sort (ThunarListModel *store)
 
 
 static void
-thunar_list_model_file_changed (ThunarFile      *file,
-                                ThunarListModel *store)
+thunar_list_model_file_changed (ThunarFileMonitor *file_monitor,
+                                ThunarFile        *file,
+                                ThunarListModel   *store)
 {
   GtkTreePath *path;
   GtkTreeIter  iter;
@@ -1141,10 +1142,13 @@ thunar_list_model_file_changed (ThunarFile      *file,
 
   g_return_if_fail (THUNAR_IS_FILE (file));
   g_return_if_fail (THUNAR_IS_LIST_MODEL (store));
+  g_return_if_fail (THUNAR_IS_FILE_MONITOR (file_monitor));
 
+  /* check if we have a row for that file */
   for (n = 0, row = store->rows; row != NULL; ++n, row = row->next)
     if (G_UNLIKELY (row->file == file))
       {
+        /* generate the iterator for this row */
         iter.stamp = store->stamp;
         iter.user_data = row;
 
@@ -1158,8 +1162,6 @@ thunar_list_model_file_changed (ThunarFile      *file,
 
         return;
       }
-
-  g_assert_not_reached ();
 }
 
 
@@ -1224,8 +1226,6 @@ thunar_list_model_files_added (ThunarFolder    *folder,
         {
           row = g_chunk_new (Row, store->row_chunk);
           row->file = file;
-          g_signal_connect_closure_by_id (G_OBJECT (file), store->file_changed_id,
-                                          0, store->file_changed_closure, TRUE);
 
           /* find the position to insert the file to */
           if (G_UNLIKELY (store->rows == NULL || thunar_list_model_cmp (store, file, store->rows->file) < 0))
@@ -1548,8 +1548,6 @@ thunar_list_model_set_folder (ThunarListModel *store,
           row = store->rows;
 
           /* delete data associated with this row */
-          g_signal_handlers_disconnect_matched (G_OBJECT (row->file), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE,
-                                                store->file_changed_id, 0, store->file_changed_closure, NULL, NULL);
           g_object_unref (G_OBJECT (row->file));
 
           /* remove the row from the list */
@@ -1611,8 +1609,6 @@ thunar_list_model_set_folder (ThunarListModel *store,
                 {
                   row = g_chunk_new (Row, store->row_chunk);
                   row->file = file;
-                  g_signal_connect_closure_by_id (G_OBJECT (file), store->file_changed_id,
-                                                  0, store->file_changed_closure, TRUE);
                   row->next = store->rows;
 
                   store->rows = row;
@@ -1753,8 +1749,6 @@ thunar_list_model_set_show_hidden (ThunarListModel *store,
 
           row = g_chunk_new (Row, store->row_chunk);
           row->file = file;
-          g_signal_connect_closure_by_id (G_OBJECT (file), store->file_changed_id,
-                                          0, store->file_changed_closure, TRUE);
 
           if (G_UNLIKELY (store->rows == NULL || thunar_list_model_cmp (store, file, store->rows->file) < 0))
             {
-- 
GitLab